• 售前

  • 售后

热门帖子
入门百科

JWT 工具类

[复制链接]
乔峰之逆风痰 显示全部楼层 发表于 2022-1-12 12:14:20 |阅读模式 打印 上一主题 下一主题
生成JWT
解析JWT
第一步:创建SpringBoot项目



  • 添加依赖
  1. <dependency>
  2.     <groupId>org.projectlombok</groupId>
  3.     <artifactId>lombok</artifactId>
  4.     <version>1.18.22</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>io.jsonwebtoken</groupId>
  8.     <artifactId>jjwt-api</artifactId>
  9.     <version>0.11.2</version>
  10. </dependency>
  11. <dependency>
  12.     <groupId>io.jsonwebtoken</groupId>
  13.     <artifactId>jjwt-impl</artifactId>
  14.     <version>0.11.2</version>
  15.     <scope>runtime</scope>
  16. </dependency>
  17. <dependency>
  18.     <groupId>io.jsonwebtoken</groupId>
  19.     <artifactId>jjwt-jackson</artifactId>
  20.     <version>0.11.2</version>
  21.     <scope>runtime</scope>
  22. </dependency>
  23. <dependency>
  24.     <groupId>org.springframework.boot</groupId>
  25.     <artifactId>spring-boot-starter-security</artifactId>
  26. </dependency>
复制代码


  • 修改配置文件application.yml
  1. <code>jwt:
  2.   # 为JWT基础信息加密和解密的密钥,长度需要大于等于43
  3.   # 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改
  4.   secret: oQZSeguYloAPAmKwvKqqnifiQatxMEPNOvtwPsCLasd
  5.   # JWT令牌的有效时间,单位秒,默认2周
  6.   expiration: 1209600
  7.   header: Authorization
  8.   prefix: hc
复制代码
第二步:创建JWT工具类

  1. @Slf4j
  2. @Component
  3. @ConfigurationProperties(prefix = "jwt")
  4. public class JwtUtil {
  5.     /**
  6.      * 携带JWT令牌的HTTP的Header的名称,在实际生产中可读性越差越安全
  7.      */
  8.     @Getter
  9.     @Value("${header}")
  10.     private String header;
  11.     /**
  12.      *
  13.      */
  14.     @Getter
  15.     @Value("${prefix}")
  16.     private String prefix;
  17.     /**
  18.      * 为JWT基础信息加密和解密的密钥
  19.      * 在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改。
  20.      */
  21.     @Value("${secret}")
  22.     private String secret;
  23.     /**
  24.      * JWT令牌的有效时间,单位秒
  25.      * - 默认2周
  26.      */
  27.     @Value("${expiration}")
  28.     private Long expiration;
  29.     /**
  30.      * SecretKey 根据 SECRET 的编码方式解码后得到:
  31.      * Base64 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));
  32.      * Base64URL 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));
  33.      * 未编码:SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
  34.      */
  35.     private static SecretKey getSecretKey(String secret) {
  36.         byte[] encodeKey = Decoders.BASE64.decode(secret);
  37.         return Keys.hmacShaKeyFor(encodeKey);
  38.     }
  39.     /**
  40.      * 用claims生成token
  41.      *
  42.      * @param claims 数据声明,用来创建payload的私有声明
  43.      * @return token 令牌
  44.      */
  45.     private String generateToken(Map<String, Object> claims) {
  46.         SecretKey key = getSecretKey(secret);
  47.         //SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); //两种方式等价
  48.         // 添加payload声明
  49.         JwtBuilder jwtBuilder = Jwts.builder()
  50.                 // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
  51.                 .setClaims(claims)
  52.                 // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
  53.                 .setId(UUID.randomUUID().toString())
  54.                 // iat: jwt的签发时间
  55.                 .setIssuedAt(new Date())
  56.                 // 你也可以改用你喜欢的算法,支持的算法详见:https://github.com/jwtk/jjwt#features
  57.                 // SignatureAlgorithm.HS256:指定签名的时候使用的签名算法,也就是header那部分
  58.                 .signWith(key, SignatureAlgorithm.HS256)
  59.                 .setExpiration(new Date(System.currentTimeMillis() + this.expiration * 1000));
  60.         String token = jwtBuilder.compact();
  61.         return token;
  62.     }
  63.     /**
  64.      * 生成Token令牌
  65.      *
  66.      * @param userDetails 用户
  67.      * @return 令牌Token
  68.      */
  69.     public String generateToken(UserDetails userDetails) {
  70.         Map<String, Object> claims = new HashMap<>();
  71.         claims.put("sub", userDetails.getUsername());
  72.         claims.put("created", new Date());
  73.         return generateToken(claims);
  74.     }
  75.     /**
  76.      * 从token中获取数据声明claim
  77.      *
  78.      * @param token 令牌token
  79.      * @return 数据声明claim
  80.      */
  81.     public Claims getClaimsFromToken(String token) {
  82.         try {
  83.             SecretKey key = getSecretKey(secret);
  84.             Claims claims = Jwts.parser()
  85.                     .setSigningKey(key)
  86.                     .parseClaimsJws(token)
  87.                     .getBody();
  88.             return claims;
  89.         } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {
  90.             log.error("token解析错误", e);
  91.             throw new IllegalArgumentException("Token invalided.");
  92.         }
  93.     }
  94.     /**
  95.      * 从token中获取登录用户名
  96.      *
  97.      * @param token 令牌
  98.      * @return 用户名
  99.      */
  100.     public String getUsernameFromToken(String token) {
  101.         String username;
  102.         try {
  103.             Claims claims = getClaimsFromToken(token);
  104.             username = claims.getSubject();
  105.         } catch (Exception e) {
  106.             username = null;
  107.         }
  108.         return username;
  109.     }
  110.     /**
  111.      * 获取token的过期时间
  112.      *
  113.      * @param token token
  114.      * @return 过期时间
  115.      */
  116.     public Date getExpirationFromToken(String token) {
  117.         return getClaimsFromToken(token).getExpiration();
  118.     }
  119.     /**
  120.      * 判断token是否过期
  121.      *
  122.      * @param token 令牌
  123.      * @return 是否过期:已过期返回true,未过期返回false
  124.      */
  125.     public Boolean isTokenExpired(String token) {
  126.         Date expiration = getExpirationFromToken(token);
  127.         return expiration.before(new Date());
  128.     }
  129.     /**
  130.      * 验证令牌:判断token是否非法
  131.      *
  132.      * @param token       令牌
  133.      * @param userDetails 用户
  134.      * @return 如果token未过期且合法,返回true,否则返回false
  135.      */
  136.     public Boolean validateToken(String token, UserDetails userDetails) {
  137.         //如果已经过期返回false
  138.         if (isTokenExpired(token)) {
  139.             return false;
  140.         }
  141.         String usernameFromToken = getUsernameFromToken(token);
  142.         String username = userDetails.getUsername();
  143.         return username.equals(usernameFromToken);
  144.     }
  145. }
复制代码
第三步:SpringSecurity配置文件注入PasswordEncoder

  1. @Configuration
  2. public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  3.     @Bean
  4.     public PasswordEncoder passwordEncoder() {
  5.         return new BCryptPasswordEncoder();
  6.     }
  7. }
复制代码
第四步:工具类测试

  1. @SpringBootTest
  2. public class JwtUtilTest {
  3.     @Resource
  4.     private JwtUtil jwtUtil;
  5.     @Resource
  6.     private PasswordEncoder passwordEncoder;
  7.     @Test
  8.     void fun(){
  9.         System.out.println(passwordEncoder);
  10.         SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
  11.         System.out.println(secretKey);
  12.     }
  13.     //生成token
  14.     @Test
  15.     void generateToken(){
  16.         //用户信息
  17.         String encode = passwordEncoder.encode("1234");
  18.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  19.         String token = jwtUtil.generateToken(user);
  20.         System.out.println(token);
  21.     }
  22.     @Test
  23.     void getClaimsFromToken(){
  24.         //用户信息
  25.         String encode = passwordEncoder.encode("1234");
  26.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  27.         String token = jwtUtil.generateToken(user);
  28.         System.out.println(token);
  29.         Claims claims = jwtUtil.getClaimsFromToken(token);
  30.         System.out.println(claims);
  31.     }
  32.     @Test
  33.     void getSubjectFromToken(){
  34.         //用户信息
  35.         String encode = passwordEncoder.encode("1234");
  36.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  37.         String token = jwtUtil.generateToken(user);
  38.         System.out.println(token);
  39.         String username = jwtUtil.getSubjectFromToken(token);
  40.         System.out.println(username);
  41.     }
  42.     @Test
  43.     void getExpirationFromToken(){
  44.         //用户信息
  45.         String encode = passwordEncoder.encode("1234");
  46.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  47.         String token = jwtUtil.generateToken(user);
  48.         System.out.println(token);
  49.         Date date = jwtUtil.getExpirationFromToken(token);
  50.         System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(date));
  51.     }
  52.     @Test
  53.     void isTokenExpired(){
  54.         //用户信息
  55.         String encode = passwordEncoder.encode("1234");
  56.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  57.         String token = jwtUtil.generateToken(user);
  58.         System.out.println(token);
  59.         Boolean res = jwtUtil.isTokenExpired(token);
  60.         System.out.println(res);
  61.     }
  62.     @Test
  63.     void validateToken(){
  64.         //用户信息
  65.         String encode = passwordEncoder.encode("1234");
  66.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  67.         String token = jwtUtil.generateToken(user);
  68.         System.out.println(token);
  69.         User user2 = new User("zhangsan", "", AuthorityUtils.createAuthorityList());
  70.         Boolean res = jwtUtil.validateToken(token,user2);
  71.         System.out.println(res);
  72.     }
  73.     //模拟篡改
  74.     @Test
  75.     void fake(){
  76.         // 将我改成你生成的token的第一段(以.为边界)
  77.         String encodedHeader = "eyJhbGciOiJIUzI1NiJ9";
  78.         // 测试4: 解密Header
  79.         byte[] header = Base64.decodeBase64(encodedHeader.getBytes());
  80.         System.out.println(new String(header));
  81.         // 将我改成你生成的token的第二段(以.为边界)
  82.         String encodedPayload = "eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk1NDEsImV4cCI6MTU2Njc5OTE0MX0";
  83.         // 测试5: 解密Payload
  84.         byte[] payload = Base64.decodeBase64(encodedPayload.getBytes());
  85.         System.out.println(new String(payload));
  86.         //用户信息
  87.         String encode = passwordEncoder.encode("1234");
  88.         User user = new User("zhangsan", encode, AuthorityUtils.createAuthorityList());
  89.         // 测试6: 这是一个被篡改的token,因此会报异常,说明JWT是安全的
  90.         jwtUtil.validateToken("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEiLCJpYXQiOjE1NjU1ODk3MzIsImV4cCI6MTU2Njc5OTMzMn0.nDv25ex7XuTlmXgNzGX46LqMZItVFyNHQpmL9UQf-aUx",user);
  91.     }
  92. }
复制代码
来源:https://blog.caogenba.net/lianghecai52171314/article/details/122429240
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作