基于JWT(JSON Web Token)的token身份验证

8,955 阅读6分钟

相逢便是缘,路过点个赞 ^.^


源码:https://github.com/yulc-coding/java-note/tree/master/jwt

介绍

JWT是一种用于通信双方之间传递安全信息的简洁的、URL安全的表述性声明规范,经常用在跨域身份验证。因为存在数字签名,因此可以起到防串改的作用

传统session模式

相对于传统的session认证,通常将session保存在服务端,需要服务器去维护。并且在服务器集群或请求服务跨域的情况下,需要共享session,使每台服务器都能读取session,比如将session持久化,增加了开销。

jwt token模式

用户登录后服务器将相关数据生成一个token返回客户端
客户端每次发起请求带上token
服务器获取token后校验token验证合法性

格式

  • Header 头信息
{
  "alg""Algorithm  加密方法:HS256",
  "cty""Content Type ",
  "typ""Type" ,
  "kid""Key Id"
 }
  • Payload 载体信息:数据包放在这里
{
  "iss""Issuer JWT的签发者",
  "aud""Audience 接收JWT的一方",
  "sub""Subject JWT的主题",
  "exp""Expiration Time JWT的过期时间",
  "nbf""Not Before 在xxx之间,该JWT都是可用的",
  "iat""Issued At 该JWT签发的时间",
  "jti""JWT ID JWT的唯一身份标识",
  "xxx""自定义属性"
}
  • Signature 签名信息 = 加密算法(header + "." + payload, 密钥)

  • TOKEN

base64(Header).base64(Payload).Signature

代码

pom

    <!-- JWT 支持-->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.3</version>
    </dependency>

    <!-- 很好用的一个工具类包 这里用来处理json和AES加密-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.0.3</version>
    </dependency>

创建token

可以传入公有声明的字段,也可以传入自定义的字段

   /**
     * 创建token
     *
     * @param json 需要放入token的参数,多个参数可以封装成json或者map
     * @return token
     */

    public static String createToken(JSONObject json) {
        try {
            // 加密方式
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    .withSubject(json.toString())
                    .withIssuer("ylc")
                    // 设置过期时间为1分钟后
                    .withExpiresAt(DateUtil.offsetMinute(new Date(), 1))
                    .withClaim("customString""自定义参数")
                    .withArrayClaim("customArray"new Integer[]{123})
                    .sign(algorithm);
        } catch (JWTCreationException exception) {
            //Invalid Signing configuration / Couldn't convert Claims.
            System.out.println(exception.getMessage());
            return null;
        }
    }

token校验

包含:
格式校验:header.payload.signature
加密方式校验: Header中的alg值
签名信息Signature校验,防止数据被篡改
载体Payload 中公有声明字段校验,如iss,jti,exp过期时间的校验

    /**
     * 校验token 合法性
     *
     * @param token to verify.
     */

    public static boolean verifyToke(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    // 验证签发人是否相同
                    .withIssuer("ylc")
                    .build();
            /*
             * 校验:
             * 格式校验:header.payload.signature
             * 加密方式校验 Header中的alg
             * 签名信息校验,防串改
             * 载体Payload 中公有声明字段校验
             */

            verifier.verify(token);
            return true;
        } catch (JWTVerificationException exception) {
            //Invalid signature/claims
            System.out.println(exception.getMessage());
            return false;
        }
    }

解析token

可以通过jwt.getClaims() 获取所有声明字段
也可以通过 jwt.getClaim(name) 获取指定名称的声明字段

/**
 * 解析token
 *
 * @param token to decode.
 */

public static void decodeToken(String token{
    try {
        DecodedJWT jwt = JWT.decode(token);
        Map<String, Claim> claims = jwt.getClaims();
        Claim customStringClaim = claims.get("customString");
        Claim customArrayClaim = claims.get("customArray");

        String issuer = jwt.getIssuer();
        String subject = jwt.getSubject();

        System.out.println(customStringClaim.asString());
        System.out.println(Arrays.toString(customArrayClaim.asArray(Integer.class)));
        System.out.println(issuer);
        System.out.println(JSONUtil.parseObj(subject));

    } catch (JWTDecodeException exception) {
        //Invalid token
        System.out.println(exception.getMessage());
    }
}

缺点

  • 默认生成的token不加密,别人可以解析token获取到其中的数据,如果要传递敏感信息,可以先将信息加密后再放入token,或者将生成的token进行加密
  • 每次延长token有效期,会从新生成一个token,需要前端替换原有的token
  • 由于服务器不保存 session状态,因此无法在使用过程中废止某个token,或者更改 token的权限。也就是说,一旦JWT签发了,在到期之前就会始终有效,需要在服务端设置相应的业务逻辑去处理。

求关注