【Java web】JWT学习笔记

什么是JWT

JWT(Json Web Tolen)是专门用来校验用户身份的,因为http请求是无状态的,每次请求都是相互独立的,所以需要借助其他方法来标记请求状态,JWT能很好的解决这个问题。

工作流程

  • 客户端使用用户名和密码请求登录
  • 服务端收到请求,验证用户名和密码
  • 验证成功后,服务端会签发一个token,再把这个token返回给客户端
  • 客户端收到token后可以把它存储起来,比如放到cookie中
  • 客户端每次向服务端请求资源时需要携带服务端签发的token,可以在cookie或者header中携带
  • 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求数据

JWT 组成

JWT生成编码后的样子

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q
header

记录令牌类型、签名算法【"alg":"HS256","type":"JWT"】

  • 声明类型,这里是jwt

  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

{

  'typ': 'JWT',

  'alg': 'HS256'

}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

Payload(有效载荷)

携带一些自定义信息,例如:{"id":"1"}

定义一个payload:

{

  "sub": "1234567890",

  "name": "John Doe",

  "admin": true

}

然后将其进行base64加密,得到Jwt的第二部分

Signature

Signature("签名",防止Token被篡改,确保安全性)这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串(头部在前),然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。

JWT使用

1、引入依赖
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>
2. 生成token
package com.example.challengecup.utils;


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JWTUtils {
   /**
    * 生成jwt
    * 使用Hs256算法, 私匙使用固定秘钥
    *
    * @param secretKey jwt秘钥
    * @param ttlMillis jwt过期时间(毫秒)
    * @param claims    设置的信息
    * @return
    */
   public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
      // 指定签名的时候使用的签名算法,也就是header那部分
      SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

      // 生成JWT的时间
      long expMillis = System.currentTimeMillis() + ttlMillis;
      Date exp = new Date(expMillis);

      // 设置jwt的body
      JwtBuilder builder = Jwts.builder()
              // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
              .setClaims(claims)
              // 设置签名使用的签名算法和签名使用的秘钥
              .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
              // 设置过期时间
              .setExpiration(exp);

      return builder.compact();
   }

   /**
    * Token解密
    *
    * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
    * @param token     加密后的token
    * @return
    */
   public static Claims parseJWT(String secretKey, String token) {
      // 得到DefaultJwtParser
      Claims claims = Jwts.parser()
              // 设置签名的秘钥
              .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
              // 设置需要解析的jwt
              .parseClaimsJws(token).getBody();
      return claims;
   }

}
3.解析token
   public static Claims parseJWT(String secretKey, String token) {
      // 得到DefaultJwtParser
      Claims claims = Jwts.parser()
              // 设置签名的秘钥
              .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
              // 设置需要解析的jwt
              .parseClaimsJws(token).getBody();
      return claims;
   }

}

技术细节

服务端每次接受到请求后都要检验一下请求携带的token,难道所有处理请求的方法都要加上校验Token的代码吗?当然不是了。下面提供两种解决方法 :Servlet提供的过滤器filter,和属于SpringMVC技术的Interceptor,具体内容请看下次更新。

猜你喜欢

转载自blog.csdn.net/weixin_55939638/article/details/134321512