欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JWT的介绍、代码实现与解决方案

程序员文章站 2022-03-31 11:09:31
...

简介

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单

 

一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

载荷就是存放有效信息的地方

签证信息由3部分组成

header (base64后的)

payload (base64后的)

secret

 

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

 

优点

体积小,因而传输速度更快

多样化的传输方式,可以通过URL传输、POST传输、请求头Header传输(常用)

简单方便,服务端拿到jwt后无需再次查询数据库校验token可用性,也无需进行redis缓存校验

在分布式系统中,很好地解决了单点登录问题

很方便的解决了跨域授权问题,因为跨域无法共享cookie

 

缺点

因为JWT是无状态的,因此服务端无法控制已经生成的Token失效,是不可控的,这一点对于是否使用jwt是需要重点考量的

获取到jwt也就拥有了登录权限,因此jwt是不可泄露的,网站最好使用https,防止中间攻击偷取jwt

在退出登录 / 修改密码时 token仍生效。下面有解决方案

 

核心代码

    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.2.0</version>
    </dependency>
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt</artifactId>
      <version>0.9.1</version>
    </dependency>
package com.mtgg.utils;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;

@Slf4j
public class JwtUtils {

    public static void main(String[] args) throws InterruptedException {
        String x = createJWT("mtgg", "", "1", 120 * 1000);
        Claims claims = parseJWT(x);
        System.out.println(JSON.toJSON(claims));
        claims.setIssuer("lskdfjl");

        Claims claims1 = parseJWT(x);
        System.out.println(JSON.toJSON(claims1));
    }


    /**
     * 签发JWT
     *
     * @param
     * @param subject   可以是JSON数据 尽可能少
     * @param ttlMillis
     * @return String
     */
    public static String createJWT(String subject, String roles, String version, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
//                .setId(id)      //唯一编号
                .setSubject(subject)   // 主题
                .setIssuer("user")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .claim("roles", roles)      //自定义
                .signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            builder.setExpiration(expDate); // 过期时间
        }
        String jwt = builder.compact();
        System.out.println("签发的jwt: " + jwt);
        return jwt;
    }


    /**
     * @Auther: Gale Li
     * @Date: 2020/2/19 19:50
     * @Description:
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.decode("MTGG2#J2*qlk64&HYU");
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }


    /**
     * 解析JWT字符串
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) {
        try {
            SecretKey secretKey = generalKey();
            return Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (ExpiredJwtException e) {
            log.error("过期异常:{}", e.toString());
            return null;
        } catch (Exception e) {
            log.error("解析异常:「」:{}", e.toString());
            return null;
        }
    }
}

 

如何主动使token失效方案?

将 token 存入 DB(如 Redis)中,失效则删除;但增加了一个每次校验时候都要先从 DB 中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则(这不就和 session 一样了么?)。

维护一个 token 黑名单,失效则加入黑名单中。个人还是推荐这种方式,可以设置token到redis中,其过期时间可以就是当初用JWT生成token时的失效时间,因为先生成后设置黑名单,所以黑名单中的token一定在JWT的token失效时间之后

 

网上还有说白名单,同理;还有更改secret的,每个用户都有一个自己的secret,想了一下一样要借助外部记录自己的secret,在更改的时候才会起作用

比较尴尬的token存储的数据不能修改,如果找到一种方式修改,那么不依赖外物可修改本身的一个version,解密的时候验证此version就可以达到验证失败的效果。如果大神有办法可以不吝赐教哦????

整体来说比较好用,jwt应该还有其他好的处理,待后续深入研究后补充

 

参考: https://www.jianshu.com/p/99a458c62aa4

 

相关标签: java羊杂demo