1.0 什么是JWT?

这是无状态通讯,信息不存放在服务器,大大地减轻了服务器压力。
我们现在了解了基于token认证的交互机制,但令牌里面究竟是什么内容?什么格式呢?市面上基于token的认证方式大都采用的是JWT(Json Web Token)。

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象(登录用户信息),传递的信息经过数字签名可以被验证和信任。

JWT令牌结构:

JWT令牌由Header、Payload、Signature三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

  • Header

头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC、SHA256或RSA)。

一个例子:

{
	"alg": "zengfl",
	"typ": "JWT"
}

将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。

  • Payload 载荷

第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比
如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。
此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。
一个例子:

{
	"sub": "1234567890",
	"name": "456",
	"admin": true
}

最后将第二部分载荷使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。

  • Signature

第三部分是签名,此部分用于防止jwt内容被篡改。
这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明
签名算法进行签名。
一个例子:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret(密钥))

base64UrlEncode(header):jwt令牌的第一部分。
base64UrlEncode(payload):jwt令牌的第二部分。
secret:签名所使用的密钥。

下图中包含一个生成的jwt令牌:

jwt令牌python jwt令牌组成_jwt令牌python

导入依赖

<!--jwt-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

干脆做一个工具类

public class AppJwtUtil {
    // TOKEN的有效期一单位秒(S)3600就是一个小时
    private static final long TOKEN_TIME_OUT = 3600;
    // 加密KEY 密钥的铭文
    private static final String TOKEN_ENCRY_KEY
            = "QQZzj881113";
    // 最小刷新间隔(S)
    private static final int REFRESH_TIME = 300;

    // 生产ID  根据用户id生成token
    public static String createToken(Long id) {
        Map<String, Object> claimMaps = new HashMap<>();
        //存放基本信息,不可以放密码等敏感信息
        claimMaps.put("id", id);
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(new Date(currentTime))  //签发时间
            .setSubject("system")  //说明
            .setIssuer("liangge") //签发者信息
            .setAudience("app")  //接收用户
            .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
            .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式,通过明文生成密文密钥
            //过期一个小时
            .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
            .addClaims(claimMaps) //cla信息
            .compact();
    }

    /**
     * 获取token中的claims信息
     * 通过token解析出里面的信息
     * @param token
     * @return
     */
    private static Jws<Claims> getJws(String token) {
        return Jwts.parser()
            .setSigningKey(generalKey())
            .parseClaimsJws(token);
    }

    /**
     * 获取payload body信息
     *
     * @param token
     * @return
     */
    public static Claims getClaimsBody(String token) {
        return getJws(token).getBody();
    }

    /**
     * 获取hearder body信息
     *
     * @param token
     * @return
     */
    public static JwsHeader getHeaderBody(String token) {
        return getJws(token).getHeader();
    }

    /**
     * 是否过期
     *
     * @param token
     * @return 1 有效  0 无效  2 已过期
     */
    public static int verifyToken(String token) {
        try {
            Claims claims = AppJwtUtil.getClaimsBody(token);
            return SystemConstants.JWT_OK;
        } catch (ExpiredJwtException ex) {
            return SystemConstants.JWT_EXPIRE;
        } catch (Exception e) {
            return SystemConstants.JWT_FAIL;
        }
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    public static void main(String[] args) {
        //测试,创建一个token
        String token = AppJwtUtil.createToken(1102L);
        System.out.println(token);
/*
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int i = AppJwtUtil.verifyToken(token);
        System.out.println(i);
        Claims claims = AppJwtUtil.getClaimsBody(token);
        Integer integer = AppJwtUtil.verifyToken("dsafafsa");
        System.out.println(integer);
        System.out.println(claims);*/

    }
}

后续要用到验证就用这个工具类

然后一下返回的常量设置

/**
 * @version 1.0
 * @description 系统常量配置
 * @package com.itheima.common.constants
 */
public interface SystemConstants {
    //JWT TOKEN已过期
    int JWT_EXPIRE = 2;
    //JWT TOKEN有效
    int JWT_OK = 1;
    //JWT TOKEN无效
    int JWT_FAIL = 0;
    // 用户头信息,用于传递当前登录用户的id
    String USER_HEADER_NAME="userId";

    String USER_HEADER_FROM="from";
    // 缓存查询出来的频道列表
    String REDISKEY_CHANNELLIST="CHANNEL_LIST";
}

后续在网关做全局过滤器获取请求头信息去解析就可知道用户是否登入了

用这个静态方法获取请求头

public static String getHeader(String headerName){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取路由转发的头信息
        String headerValue = request.getHeader(headerName);
        return headerValue;
    }

做一个工具类,因为这个比较常用

public class RequestContextUtil {
    public static String getHeader(String headerName){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取路由转发的头信息
        String headerValue = request.getHeader(headerName);
        return headerValue;
    }

    public static Long getUserId(){
        return Long.parseLong(RequestContextUtil.getHeader(SystemConstants.USER_HEADER_NAME));
    }
}