个人随笔
目录
什么是JWT?JWT在Java语言中的使用详解
2023-07-11 18:47:38

什么是JWT?JWT,全称JsonWebToken,是一种基于json的开发标准。下面的文章,将为大家详细地介绍一下JWT的特点、原理和数据结构以及在Java中是怎么去使用的。

JWT特点

JWT默认是不加密,但也是可以加密的。生成原始Token以后,可以用密钥再加密一次。

JWT不加密的情况下,不能将秘密数据写入JWT。

JWT不仅可以用于认证,也可以用于交换信息。有效使用JWT,可以降低服务器查询数据库的次数。

JWT的最大缺点是,由于服务器不保存session状态,因此无法在使用过程中废止某个token,或者更改token的权限。也就是说,一旦JWT签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

JWT本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

1.JWT的原理

Jwt官网:https://jwt.io/

JWT的原理是,服务器认证以后,生成一个JSON对象,发回给用户,就像下面这样。

  1. {
  2. "name":"JohnDoe",
  3. "角色":"管理员",
  4. "到期时间":"2018年7月1日0点0分"
  5. }

以后,用户与服务端通信的时候,都要发回这个JSON对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何session数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

2.JWT的数据结构

JWT大概就像下面这样。

  1. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT内部是没有换行的。

JWT的三个组成部分依次如下。

  1. ·Header(头部)
  2. ·Payload(负载)
  3. ·Signature(签名)

写成一行,就是下面的样子

  1. Header.Payload.Signature
2.1Header

Header部分是一个JSON对象,描述JWT的元数据,通常是下面的样子。

  1. {
  2. "alg":"HS256",
  3. "typ":"JWT"
  4. }

上面代码中,alg属性表示签名的算法(algorithm),默认是HMACSHA256(写成HS256);typ属性表示这个令牌(token)的类型(type),JWT令牌统一写为JWT。

最后,将上面的JSON对象使用Base64URL算法转成字符串。

2.2Payload

Payload部分也是一个JSON对象,用来存放实际需要传递的数据。JWT规定了7个官方字段,供选用。

  1. iss(issuer):签发人
  2. exp(expirationtime):过期时间
  3. sub(subject):主题
  4. aud(audience):受众
  5. nbf(NotBefore):生效时间
  6. iat(IssuedAt):签发时间
  7. jti(JWTID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子

  1. {
  2. "sub":"1234567890",
  3. "name":"JohnDoe",
  4. "admin":true
  5. }

JWT默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个JSON对象也要使用Base64URL算法转成字符串。

2.3Signature

Signature部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header里面指定的签名算法(默认是HMACSHA256),按照下面的公式产生签名。

  1. HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
  2. `

算出签名以后,把Header、Payload、Signature三个部分拼成一个字符串,每个部分之间用”点”(.)分隔,就可以返回给用户。

3.在Java中使用

3.1引入依赖
  1. <!-- Jwt https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt</artifactId>
  5. <version>0.9.1</version>
  6. </dependency>
3.2代码例子
  1. package com.suibibk.jwt;
  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import io.jsonwebtoken.impl.Base64Codec;
  6. import java.util.Date;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. class DemoApplicationTests {
  10. // 加盐秘钥
  11. private static String secret = "jwtSecretValueqweruqwyeiquweyi";
  12. public static void main(String[] args) throws InterruptedException {
  13. // 创建token
  14. String token = createToken(5);
  15. System.out.println("token: " + token);
  16. // 解析token, jwt是经过Base64编码的
  17. String[] ts = token.split("\\.");
  18. System.out.println("Header(头部): " + Base64Codec.BASE64.decodeToString(ts[0]));
  19. System.out.println("Payload(负载): " + Base64Codec.BASE64.decodeToString(ts[1]));
  20. System.out.println("Signature(签名): " + Base64Codec.BASE64.decodeToString(ts[2]));
  21. //这里休息六秒后会导致抛出异常ExpiredJwtException,表明token已经失效了
  22. //Thread.sleep(6 * 1000);
  23. // 解析token
  24. parseToken(token);
  25. }
  26. // 创建token
  27. public static String createToken(Integer time) {
  28. // 自定义信息
  29. Map<String, Object> map = new HashMap<>();
  30. map.put("name", "admin");
  31. String token = Jwts.builder()
  32. .setClaims(map)// 自定义内容接受一个map
  33. .setId("9527") // 唯一id
  34. .setSubject("jwtSubject") // JWT的主体
  35. .setExpiration(new Date(System.currentTimeMillis() + time * 1000))// 设置过期时间 time秒
  36. .setIssuedAt(new Date())//设置签发时间
  37. .signWith(SignatureAlgorithm.HS256, secret)// 设置签名算法和加盐秘钥
  38. .compact();
  39. return token;
  40. }
  41. // 解析token
  42. public static void parseToken(String token) {
  43. System.out.println("====================开始解析JWT====================");
  44. try {
  45. Claims body = Jwts.parser()
  46. .setSigningKey(secret)// 签名秘钥
  47. .parseClaimsJws(token)// 要解析的jwt
  48. .getBody();
  49. System.out.println("id: " + body.getId());
  50. System.out.println("sub: " + body.getSubject());
  51. System.out.println("自定义内容 name: " + body.get("name"));
  52. System.out.println("令牌签发时间: " + body.getIssuedAt());
  53. System.out.println("令牌过期时间: " + body.getExpiration());
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. System.out.println("无效Token");
  57. }
  58. System.out.println("====================JWT解析结束====================");
  59. }
  60. }

运行输出

  1. token: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqd3RTdWJqZWN0IiwibmFtZSI6ImFkbWluIiwiZXhwIjoxNjg5MDY2OTk3LCJpYXQiOjE2ODkwNjY5OTIsImp0aSI6Ijk1MjcifQ.-b2wY6_Zjeq0GV1xxMK7TEtYYAN_5VXwVj99MwV6CV8
  2. Header(头部): {"alg":"HS256"}
  3. Payload(负载): {"sub":"jwtSubject","name":"admin","exp":1689066997,"iat":1689066992,"jti":"9527"
  4. Signature(签名): ????[乱码,这是因为这里是签名]
  5. ====================开始解析JWT====================
  6. id: 9527
  7. sub: jwtSubject
  8. 自定义内容 name: admin
  9. 令牌签发时间: Tue Jul 11 17:16:32 CST 2023
  10. 令牌过期时间: Tue Jul 11 17:16:37 CST 2023
  11. ====================JWT解析结束====================

如果放开

  1. //Thread.sleep(6 * 1000);

我们设置的过期时间是5秒,所以休息6秒后就会抛出异常

  1. ====================开始解析JWT====================
  2. io.jsonwebtoken.ExpiredJwtException: JWT expired at 2023-07-11T17:19:57Z. Current time: 2023-07-11T17:19:59Z, a difference of 2164 milliseconds. Allowed clock skew: 0 milliseconds.
  3. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:385)
  4. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
  5. at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
  6. at com.suibibk.jwt.DemoApplicationTests.parseToken(DemoApplicationTests.java:55)
  7. at com.suibibk.jwt.DemoApplicationTests.main(DemoApplicationTests.java:30)
  8. 无效Token
  9. ====================JWT解析结束====================

我们捕获到这个异常就表明已过期了,如果有人篡改就会报签名异常

  1. ====================开始解析JWT====================
  2. io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
  3. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:354)
  4. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)
  5. at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)
  6. at com.suibibk.jwt.DemoApplicationTests.parseToken(DemoApplicationTests.java:55)
  7. at com.suibibk.jwt.DemoApplicationTests.main(DemoApplicationTests.java:30)
  8. 无效Token
  9. ====================JWT解析结束====================

参考:https://www.w3cschool.cn/article/43015292.html

 241

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2