JWT(JSON Web 令牌)

参考文献

[2] JWT令牌-CSDN博客 参考日期:2024.05.21

[1] Day12-06. 登录校验-JWT令牌-介绍哔哩哔哩bilibili 参考日期:2024.05.21

JWT 是何

概念

JWT 是 JSON Web Token 的缩写,即 JSON Web 令牌,是一种用于在网络应用程序之间 以 JSON 数据格式安全传输信息 的开放标准(RFC 7519)

JWT 是一种 轻量级 的 Token,定义了一种简洁的、自包含的格式,包含了一些关键信息,比如用户身份、权限等

由于 数字签名 的存在,这些信息是可靠的,在传输过程中 不被篡改或伪造

使用 JWT 的好处:可以方便地在不同的应用程序或服务之间共享用户身份信息,而无需每次都进行身份验证。且因为 JWT 是基于标准的 JSON 格式,易于使用和传输

构成

JWT 的格式是由 RFC 7519 标准规定的三部分组成:

  • 头部(Header):包含 Token 类型、加密算法等信息

  • 载荷(Payload):包含 了需要传输的信息,分为 自定义信息默认信息,例如用户 ID、角色、权限等

    可能称为 “携带” 更能体现 Payload 的字面意义

  • 签名(Signature):将 Header 和 Payload 加密 后得到的结果,用于验证 Token 是否被篡改,确保安全性

    加入指定秘钥,通过指定签名算法计算


假设一个网站需要验证用户身份并授权用户访问某些受保护的资源,使用 JWT,用户登录后,服务器会生成一个包含用户信息的 JWT,然后将该 Token 返回给客户端

如下所示:

 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWQiOiIxMjM0NTY3ODkwIiwicm9sZXMiOlsiYWRtaW4iLCJ1c2VyIl19.
 SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

不要被这一 “坨” 字符串吓到,这不是我们处理的,甚至无需去看,这是给计算机去处理的

JWT 接收原始 JSON 格式后,会对其根据 Base64 进行编码处理,然后再进行解码

Base64:是一种基于 64 个可打印字符(A-Z、a-z、0-9、+、-)还有一个补位字符 = 来表示二进制数据的编码方式

其中,第 1 行是 Header;第 2-3 行是 Payload;第 3 行是 Signature。三部分之间用英文 . 分隔

Header

从 Header 开始分析,这 “坨” 字符串表示的原始 JSON 格式是:

 {"alg": "HS256", "type": "JWT"}

参数含义:

  • alg:表示使用的签名算法,上例中指定为 HMAC-SHA256 算法

  • type:表示 Token 类型,上例中指定为 JWT

关于签名算法可以前往 JWT 官网 查看:

image-20240522002919113

Payload

 {
  "sub": "1234567890",  // ID
  "name": "John Doe", // 角色
  "iat": 1516239022  // 权限
 }

所有参数含义:

  • iss:JWT 签发者

  • sub:JWT 所面向的用户

  • aud:接收 JWT 的一方

  • iat:JWT 的签发时间

  • exp:JWT 的过期时间,这个过期时间必须要大于签发时间

  • nbf:在定义时间之前,该 JWT 不可用

  • jti:JWT 唯一身份标识,主要用来作为一次性 Token,从而回避重放攻击

Signature

Signature 的目的就是为了防止 Token 被篡改,确保 Token 的安全性

JWT 会根据依照 Header 中指定的 alg,即 “签名算法” 来合并前面的 Header 和 Payload,并且还会加入 我们个人自定义的密钥,然后再来编码 Signature

这个 Signature 是自动计算出来的,并不是根据 Base64 编码而成

一旦 Signature 中的某个字符被篡改,那整个 JWT 的校验都会失败。正是因为 Signature 的存在,才使得 JWT 非常安全可靠

image-20240522001723518

JWT 如何

常用场景 - 登录校验

JWT 最常用的场景就是登录校验

image-20240521235520347

登录流程:

  1. 浏览器(前端)发起登录操作,此时访问登录接口(后端)

  2. 登录校验成功,后端 生成 JWT

  3. 后端将生成的 JWT 返回给前端

  4. 前端拿到 JWT 后,会将其存储在浏览器的 localScorage 中,在 后续所有请求 中,每一次请求都会将这个 JWT 存储在请求头中随着请求发送到后端

    local scorage 本地存储

  5. 当前端再次发起请求时,后端会进行 统一拦截判断这次请求中是否携带 JWT

    统一拦截就是过滤器和拦截器的知识点了,与 JWT 本身无关

    • 没有携带 JWT:拒绝前端访问,请求响应失败

    • 携带 JWT:校验 JWT 是否有效

  6. 前端请求携带的 JWT 有效,则后端放行,正常处理请求

后端主要操作就是 生成和校验 JWT

注意:

  • 登录页面的请求不会拦截,因为后端需要根据登录信息生成 JWT

  • 后端通过解析 JWT 并验证 Signature,可以获取到 Payload 部分存储的用户的身份和权限,从而判断是否放行

基于 Java 生成和校验 JWT

在项目中引入 JWT 库

想要使用 JWT,首先就要在项目中引入 JWT 的相关依赖

在实际使用中,通常会使用现有的 JWT 库来生成和解析 JWT,常用的 JWT 库有 java-jwt、jjwt

这些库已经实现了 RFC 7519 标准中的格式和规范,因此可以方便地使用这些库来生成和解析 JWT

下面以 jjwt 为例

在项目 pom.xml 中引入 jjwt 依赖:

 <!-- JWT 库 -->
 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt</artifactId>
     <version>0.9.1</version>
 </dependency>

引入依赖后,就可以使用该工具包中的 API 来实现 JWT 的生成和校验

生成 JWT

以参考文献中黑马程序员 - Java Web 开发教程\day12-SpringBootWeb登录认证\代码\tlias-web-management 的项目为例

引入依赖后,打开测试类 TliasWebManagementApplicationTests.java

在测试类中添加测试生成 JWT 的方法:

 @Test
 public void testGenJWT(){
     // 通过 Map 集合存储生成 JWT 的载荷
     Map<String, Object> claims = new HashMap<>();
     // 自定义信息(一般是存储前端请求携带的登录信息)
     claims.put("id", 1);  // 用户 ID
     claims.put("name", "Li");  // 用户名
 ​
     // 调用 Jwts.builder() 生成 JWT,使用链式方法设置生成 JWT 时需要的参数
     String jwt = Jwts.builder()
         // 设置签名算法为 HS256,密钥自定义
         .signWith(SignatureAlgorithm.HS256, "LiiiYiAn")  
         .setClaims(claims)  // 设置载荷
         // 设置 JWT 有效期为 1h(以 ms 为单位)
         .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000))  
         .compact();  // 返回生成 JWT 根据设置信息编码后的字符串
 ​
     System.out.println("生成的 JWT 为:" + jwt);
 }

编写完成后,就可以测试运行这个方法了

这里有一个小技巧,当测试方法是独立的,即与整个 Spring 环境无关联,就可以先注释掉测试类上的注解 @SpringBootTest,这样就不会加载整个 Spring 环境,能 加快方法运行速度

image-20240522003908418

运行后输出:

 生成的 JWT 为:eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTGkiLCJpZCI6MSwiZXhwIjoxNzE2MzEyOTg0fQ.Uhqi2bGY6SBjNVRto6nv-PbpFAg1ZFZi5HLKA4GLirE

此时可以复制这串字符串,然后打开 JWT 官网,将这串字符串放在 Encoded 框中:

image-20240522004042387

此时会解析这串字符串,解析后就能得到 JWT 编码前的原始 JSON 格式信息了

参数解析:

  • alg:签名算法

  • exp:JWT 过期时间

因为 Signature 并不是 Base64 编码,是通过 Header + Payload + 签名算法计算得出的,所以不会解析出来

这是 JWT 的 “看家本领”,怎么能展示出来呢,如果解析出来了那别人岂不是能反推从而破解 JWT,那还谈什么传输信息安全性

也可以直接在网上搜索 Base64 编码解码工具,也能得到 Header 和 Payload 的解码原始 JSON 数据格式,比如 Base64 在线编码解码 | Base64 加密解密 - Base64.us

同样的,Signature 不能解码

复制 Header 部分进行解码:

image-20240522102011675

解析 JWT(校验)

在测试类中添加测试解析 JWT 的方法:

 @Test
 public void testParseJWT(){
     // 调用 Jwts.parser() 解析 JWT 中携带的载荷部分
     // 使用链式方法设置解析 JWT 时需要的参数
     Claims claims = Jwts.parser()
         // 设置解析时的签名算法密钥,必须与生成 JWT 时设置的密钥一致否则解析失败
         .setSigningKey("LiiiYiAn")
         // 解析生成 JWT 根据设置信息编码后的字符串
        .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTGkiLCJpZCI6MSwiZXhwIjoxNzE2MzEyOTg0fQ.Uhqi2bGY6SBjNVRto6nv-PbpFAg1ZFZi5HLKA4GLirE")
         // 返回解析 JWT 后载荷部分的原始 JSON 数据格式
         // 也可以使用 Jwts.getHeader() 返回头部部分
         // 不过前面的参数类型要改为 Header
         .getBody();
 ​
     System.out.println(claims);
 }

运行测试:

image-20240522103626790

报错信息:

 io.jsonwebtoken.ExpiredJwtException: JWT expired at 2024-05-22T01:36:24Z. Current time: 2024-05-22T10:33:24Z, a difference of 32220847 milliseconds.  Allowed clock skew: 0 milliseconds.
 过期JwtException:JWT于2024-05-22T01:36:24Z过期。当前时间:2024-05-22T10:33:24Z,相差32220847毫秒。允许的时钟偏移:0毫秒

就是 JWT 过期了,所以无法解码

如果 JWT 解析校验时报错,则说明 JWT 被篡改或失效了,Token 非法

这是因为我使用的是前面生成的 JWT,而当时设置的 JWT 有效时间是 1h,而这次解析 JWT 已经过了 1h,所以 JWT 失效了

此时我们应该重新去运行前面生成 JWT 的方法,然后复制生成的 JWT,替换掉解析 JWT 中的 JWT 字符串:

 .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTGkiLCJpZCI6MSwiZXhwIjoxNzE2MzQ5MDQxfQ.4T3cw6vzdjQffK2vBrokxsX7-nPNuKFPol8jGlEVYxk")

再次运行:

image-20240522104707105

解析 JWT 中的载荷部分成功

同时,一旦 JWT 生成了,我们篡改其中的任何一个字符都会解析失败,可以尝试修改后运行

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值