掌握JWT:解密身份验证和授权的关键技术

JSON Web Token

  • 1、什么是JWT
  • 2、JWT解决了什么问题
  • 3、早期的SSO认证
  • 4、JWT认证
  • 5、JWT优势
  • 6、JWT结构
          • Header 标头
          • Payload 负载
    • Signature 签名
  • 7、代码实现
    • 添加依赖
    • 生成Token
    • 认证token
  • 8、工具类
  • 9、JWT整合Web
  • 10、拦截器校验
  • 11、网关路由校验
  • 12、解决多用户登录的问题
  • 13、客户端保存/携带token
  • 14、抽取ajax工具类
  • 15、a标签跳转如何传递token

1、什么是JWT

官方文档解释:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

官网地址: https://jwt.io/introduction/

通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,客户端请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据。

2、JWT解决了什么问题

  • 授权:这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛;
  • 信息交换:JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥是以对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。

3、早期的SSO认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

在这里插入图片描述

4、JWT认证

在这里插入图片描述

  • 首先,前端通过Web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密的传输(https协议)。

  • 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。形成的JWT就是一个形同aaaa.bbb.cc的字符串。 token head.payload.singurater。

  • 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。

  • 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决XSS和XSRF问题) HEADER。

  • 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己(可选)。

  • 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果。

5、JWT优势

JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点:

  • 简洁(Compact)

可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快。

  • 自包含(Self-contained)

负载中包含了所有用户所需要的信息,避免了多次查询数据库。

  • 自校验

对token可以自己校验是否过期。

6、JWT结构

令牌组成

  • 标头(Header)
  • 有效载荷(Payload)
  • 签名(Signature)
Header 标头

​ 标头通常由两部分组成:令牌的类型(即JWT)和所使用的签名算法。它会使用 Base64 对header做编码,组成而来JWT结构的第一部分。
Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。

{"alg": "HS256", # 签名算法"typ": "JWT" # 类型
}
Payload 负载

​ 这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,常用的由 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。同样的,它也会使用 Base64 编码组成 JWT 结构的第二部分。

{"iss": "demo JWT","iat": 1342513302,"exp": 1342513302,"name": "admin","sub": "dev"
}

Signature 签名

前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。

三个部分通过.连接在一起就是我们的 JWT 了

签名的目的:

最后一步签名的过程,实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

信息安全性:

在这里大家一定会问一个问题:Base64是一种编码,是可逆的,那么我的信息不就被暴露了吗?
是的。所以,在JWT中,不应该在负载里面加入任何敏感的数据。在上面的例子中,我们传输的是用户的User ID。这个值实际上不是什么敏感内容,一般情况下被知道也是安全的。但是像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了。
因此JWT适合用于向Web应用传递一些非敏感信息。JWT还经常用于设计用户认证和授权系统,甚至实现Web应用的单点登录。

在这里插入图片描述

7、代码实现

添加依赖

		<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>

生成Token

	@Testvoid testCreateToken() {// 1.设置超时时间Calendar calendar = Calendar.getInstance();calendar.add(Calendar.SECOND,30); // 超时时间是30s// 2.创建JWTbuilderJWTCreator.Builder builder = JWT.create();// 3.设置头,负载,签名String token = builder
//				.withHeader(map) 设置头信息,可以不设置有默认值.withClaim("name", "admin").withClaim("id", 10) // 设置用户自定义属性.withExpiresAt(calendar.getTime()) // 设置令牌超时时间.sign(Algorithm.HMAC256("dalaoshi"));// 设置用户签名// 4.输出结果System.out.println(token);}

认证token

     		String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWRtaW4iLCJpZCI6MTAsImV4cCI6MTU5OTQwNTQ2NH0.7YFYieOC-ChS32He7DqyVtECCvM4nFWmb7hKLiPAIXY\n";// 1.根据用户签签名获取JTW校验器JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("dalaoshi")).build();// 2.验证tokenDecodedJWT verify = jwtVerifier.verify(token);// 3.获取token的数据System.out.println(verify.getClaim("name").asString()); // 字符串使用asString()System.out.println(verify.getClaim("id").asInt()); // int使用asIntSystem.out.println(verify.getExpiresAt()); // 获取过期时间

认证常见的异常
在这里插入图片描述

- SignatureVerificationException:		签名不一致异常
- TokenExpiredException:    			令牌过期异常
- AlgorithmMismatchException:			算法不匹配异常
- InvalidClaimException:				失效的payload异常

8、工具类

public class JWTUtils {private static String sign = "dalaoshi";public static String createToken(Map<String, String> map) {// 1.设置超时时间Calendar calendar = Calendar.getInstance();calendar.add(Calendar.DATE, 7); // 7天// 2.创建JWTbuilderJWTCreator.Builder builder = JWT.create();// 设置负载数据Set<Map.Entry<String, String>> entries = map.entrySet();for (Map.Entry<String, String> entrie : entries) {builder.withClaim(entrie.getKey(), entrie.getValue());}// 3.设置签名,过期时间String token = builder.withExpiresAt(calendar.getTime()) // 设置令牌超时时间.sign(getSignature());// 设置用户签名// 4.返回return token;}// 获取起签名public static Algorithm getSignature() {return Algorithm.HMAC256(sign);}// 校验public static DecodedJWT require(String token) {return JWT.require(getSignature()).build().verify(token);}// 获取token中的数据public static Claim getPayload(String token, String key) {return require(token).getClaim(key);}
}

9、JWT整合Web

    @Autowiredprivate IUserService userService;@RequestMapping("/login")public ResultEntity login(String username,String password){ResultEntity resultEntity = userService.login(username, password);if(ResultEntity.SUCEESS.equals(resultEntity.getStatus())){Map<String,String> map = new HashMap<>();map.put("id","10");map.put("username",username);String token = JWTUtils.createToken(map);return ResultEntity.success(token);}else{return ResultEntity.error("登录失败");}}@RequestMapping("/require")public ResultEntity require(String token){try {DecodedJWT require = JWTUtils.require(token);return ResultEntity.response(require);}catch (TokenExpiredException e){return ResultEntity.error("token过期");}catch (SignatureVerificationException e){return ResultEntity.error("用户签名不一致");} catch (InvalidClaimException e){return ResultEntity.error("payload数据有误");}catch (Exception e){return ResultEntity.error("校验失败");}}@RequestMapping(value = "/getPayLoad")public ResultEntity getPayLoad(String token){DecodedJWT decodedJWT = JWTUtils.require(token);Map<String, Claim> claims = decodedJWT.getClaims();Map<String,String> map = new HashMap<>();Set<Map.Entry<String, Claim>> entries = claims.entrySet();for (Map.Entry<String, Claim> entrie:entries) {map.put(entrie.getKey(),entrie.getValue().asString());}return ResultEntity.success(map);}

10、拦截器校验

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取tokenString token = request.getHeader("token");Map<String,Object> map = new HashMap<>();try {// 2.校验JWTUtils.verify(token);return true;}catch (TokenExpiredException e){return ResultEntity.error("token过期");}catch (SignatureVerificationException e){return ResultEntity.error("用户签名不一致");} catch (InvalidClaimException e){return ResultEntity.error("payload数据有误");}catch (Exception e){return ResultEntity.error("校验失败");}// 3.校验失败响应数据String json = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().println(json);return false;
}

11、网关路由校验

@Component
public class SSOFilter extends ZuulFilter{@Autowiredprivate ISSOService ssoService;@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}@Overridepublic int filterOrder() {return FilterConstants.PRE_DECORATION_FILTER_ORDER-1;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext requestContext = RequestContext.getCurrentContext();HttpServletRequest request = requestContext.getRequest();StringBuffer requestURL = request.getRequestURL();System.out.println(requestURL);// 1.该服务是否需要验证if("http://localhost/shop-back/user/getUserPage".equals(requestURL.toString())){String token = request.getHeader("token");// 2.验证服务ResultEntity resultEntity = ssoService.require(token);System.out.println(resultEntity);if(!ResultEntity.SUCEESS.equals(resultEntity.getStatus())){requestContext.setSendZuulResponse(false); // 不能往下执行了HttpServletResponse response = requestContext.getResponse();response.setContentType("application/json;charset=utf-8"); // 设置响应数据类型requestContext.setResponseBody(JSON.toJSONString(ResultEntity.error("校验未通过"))); // 设置响应数据}}return null;}
}

12、解决多用户登录的问题

如果一个用户登录在多个设备登录,就会出现一个用户多个token在多个设备上同时登录。如果要解决这个问题就要判断用户操作的token是否是最新的,只有是最新的token才能认证成功。

// 伪代码// login
public String login(String name,String password){// 1.查询数据库认证// 2.生成tokenString token = "";// 3.把用户最新的token放入到reids中redisTemp.set(username,token); // username作为key,多次登录key会被覆盖
}// 路由校验// 1.获取用户token
// 2.根据用户名查询用户最新的token
// 3.对比两个token是否一致,如果不一致就说明用户进行了第二次登陆,就不让认证通过。

13、客户端保存/携带token

// 登录获取token,保存到本地  
function login(){var username ="admin";var password ="123";var param = new Object();param.username=username;param.password=password;$.post("http://localhost/shop-sso/sso/login",param,function (data) {if(data.status ="success"){// 获取tokenvar token = data.data;// 保存toke到客户端localStorage.setItem("login-token",token);}},"JSON");}// 发送请求是把token放到请求头中保存function sendRequest(){$.ajax({url: "http://localhost/shop-sso/addXxxxx",type: "post",dataType: 'json',beforeSend: function (XMLHttpRequest) {// 获取本地储存的token,添加到请求头中XMLHttpRequest.setRequestHeader("Authorization", localStorage.getItem("login-token"));},success: function (result) {}});}

为什么要把token放在请求头中的Authorization中?
a)保存在请求头中方便和其他参数区分
b)保存在请求头中可以解决跨域的问题,比如cookie是存在跨域的问题
c)Authorization header就是为用户认证而生的。
d)解决XSS和XSRF问题

14、抽取ajax工具类

window.utils={ajax:function(param){$.ajax({url: param,type: "post",dataType: 'json',data:param.data,beforeSend: function (XMLHttpRequest) {XMLHttpRequest.setRequestHeader("Authorization", localStorage.getItem("login-token"));},success: function (result) {param.success(result);}});}
}// 调用
utils.ajax({url:"http://localhost/shop-sso/sso/login",data:param,success:function(data){if(data.status ="success"){// 获取tokenvar token = data.data;// 保存toke到客户端localStorage.setItem("login-token",token);}}
})

15、a标签跳转如何传递token

token只针对api设计,和原生标签的跳转没有直接的关系。如果请求跳转可以在url后面携带token。

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/578391.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程学习(3)-volatile关键字,wait/notify的使用

​ &#x1f495;"命由我作&#xff0c;福自己求"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;线程学习(2)​​​​ 一.volatile关键字 volatile关键字是多线程编程中一个非常重要的概念&#xff0c;它主要有两个功能&#xff1a;保证内存可见性…

面向对象设计与分析40讲(15)简单工厂方法模式

文章目录 定义示例优缺点定义 简单工厂模式是一种创建型模式,用于根据客户端的需求创建对象实例,所谓的需求反映到编程语言里就是传入的参数。 简单工厂模式包括三个主要部分: 工厂类(Simple Factory):这是整个模式的核心。它负责根据客户端的请求来创建并返回相应的对…

Maven依赖管理项目构建工具

文章目录 Maven依赖管理项目构建工具目录一、Maven简介1. Maven介绍2. Maven主要作用理解3. Maven软件工作原理模型图&#xff08;了解&#xff09; 二、Maven安装和配置1. Maven安装2. Maven环境配置3. Maven功能配置4. IDEA配置本地Maven软件 三、基于IDEA创建Maven工程1. 梳…

Anylogic Pro 8.8.x for Mac / for Linux Crack

Digital twins – a step towards a digital enterprise AnyLogic是唯一一个支持创建模拟模型的方法的模拟建模工具&#xff1a;面向过程&#xff08;离散事件&#xff09;、系统动态和代理&#xff0c;以及它们的任何组合。AnyLogic提供的建模语言的独特性、灵活性和强大性使…

自恢复保险丝

自恢复保险丝 常用电子元器件类型 0467.500NRHF 文章目录 自恢复保险丝前言一、自恢复保险丝是什么二、0467.500NRHF总结前言 自恢复保险丝通常用于电子设备、电路板、电视机、计算机、通信设备、充电器、电源适配器等各种电路保护应用。需要注意的是,选择适当的自恢复保险…

Docker部署Nexus Maven私服并实现远程访问Nexus界面

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. Docker安装Nexus2. 本地访问Nexus3. Linux安装Cpolar4. 配置Nexus界面公网地址5. 远程访问 Nexus界面6. 固定N…

Failed to resolve component: router-view

出现了这个问题&#xff0c;导致本该出现的页面没有出现&#xff0c;在网上看了解决办法&#xff0c;原来是没有挂载好app 原先代码&#xff1a; app.use(router) createApp(App).mount(#app) //这是又创建了一个新的app&#xff0c;无法使用到router改进&#xff1a; app.…

【51单片机系列】DS18B20温度传感器模块

本文是关于温度传感器的相关内容。 文章目录 一、 DS18B20数字温度传感器介绍1.1、 DS18B20温度传感器的特点1.2、DA18B20内部结构1.3、 DS18B20的温度转换规则1.4、 DS18B20的ROM指令表1.6、 计算温度1.7、 读写时序 二、DS18B20使用示例 一、 DS18B20数字温度传感器介绍 DS1…

Sublime Text 4 中文汉化教程(Version: Build 4169)

Sublime Text 4汉化 1 知识小课堂1.1 sublim简介1.2 其他编辑器 2 安装过程2.1 安装Install Package Control2.2 Install Package2.3 安装工具包2.4 常用的插件2.5 安装中文包 1 知识小课堂 1.1 sublim简介 Sublime是一款代码编辑器&#xff0c;致力于为开发人员提供快速、高…

解决企业TB或者PB级大文件传输速度和安全问题

随着企业数据不断增加&#xff0c;TB或PB级大文件的传输成为企业信息共享和数据备份的重要手段之一。然而&#xff0c;这些大文件的传输速度和安全问题成为制约企业发展的瓶颈&#xff0c;也是企业需要解决的重要问题。本文将探讨如何解决这些问题&#xff0c;并从以下几个方面…

海外短剧操作配置,海外短剧小程序功能介绍,海外短剧小程序搭建开发制作

一、海外短剧小程序操作&配置 完整文档联系趣图吖&#xff1a;nn7334n 二、海外短剧小程序功能 功能: 批量导入一键改价 多语言切换:英语、泰语、印尼、西班 更新) 前端: 安卓ios h5三端 营销功能强大:积分营销分销商入驻会员模式 卡密模式 单独付费 三、海外短剧小程…

如何免费下载5天以内的卫星影像?

现在&#xff0c;我们就详细分享一下如何下载5天以内卫星影像的具体操作方法。 如何下载5天以内卫星影像 在soar.earth网站中&#xff0c;点击“Enter the Atlas”按钮&#xff0c;打开地图图库。 打开地图图库 我们可以根据自己的需要&#xff0c;在右下角切换底图&#xf…

掌握的单词个数 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 有一个字符串数组 words 和一个字符串 chars。假如可以用 chars 中的字母拼写出 words 中的某个"单词"(字符串),那么我们就认为你掌握了这个单词。 words 的字等仅由 a-z 英文小写宁母组成,例如“abc”。 char…

Pytorch深度强化学习2-1:基于价值的强化学习——DQN算法

目录 0 专栏介绍1 基于价值的强化学习2 深度Q网络与Q-learning3 DQN原理分析4 DQN训练实例 0 专栏介绍 本专栏重点介绍强化学习技术的数学原理&#xff0c;并且采用Pytorch框架对常见的强化学习算法、案例进行实现&#xff0c;帮助读者理解并快速上手开发。同时&#xff0c;辅…

SQL server 数据库练习题及答案(练习3)

一、编程题 公司部门表 department 字段名称 数据类型 约束等 字段描述 id int 主键&#xff0c;自增 部门ID name varchar(32) 非空&#xff0c;唯一 部门名称 description varchar(1024) …

HTTP content-type内容类型的常见格式

本专栏是汇集了一些HTML常常被遗忘的知识&#xff0c;这里算是温故而知新&#xff0c;往往这些零碎的知识点&#xff0c;在你开发中能起到炸惊效果。我们每个人都没有过目不忘&#xff0c;过久不忘的本事&#xff0c;就让这一点点知识慢慢渗透你的脑海。 本专栏的风格是力求简洁…

分布式训练通信NCCL之Ring-Allreduce详解

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

fpga 8段4位数码管verilator模拟

8段4位数码管verilator模拟 seg.v module seg(input wire clk,input wire rst_n,output wire[7:0] SEG,output wire[3:0] SEL );reg[7:0] digit[0:15] {8h3f, 8h06, 8h5b, 8h4f, 8h66, 8h6d, 8h7d,8h07,8h7f,8h6f, 8h77, 8h7c, 8h39, 8h5e, 8h79, 8h71};reg[31:0] cnt 32…

Opencv_CUDA实现推理图像前处理与后处理

Opencv_CUDA实现推理图像前处理与后处理 通过trt 或者 openvino部署深度学习算法时&#xff0c;往往会通过opencv的Mat及算法将图像转换为固定的格式作为输入openvino图像的前后处理后边将在单独的文章中写出今晚空闲搜了一些opencv_cuda的使用方法&#xff0c;在此总结一下前…

云服务器ECS运维管理

目录 实时掌握CPU、内存使用情况 实时掌握存储的使用情况 定期对云服务器数据做好备份 定期检查云服务器的安全运行情况 要想保证云服务器长期稳定的使用&#xff0c;除了依靠阿里云&#xff08;云服务提供商&#xff09;的技术支持&#xff0c;自身必要的安全维护手段也是…