jwt生成token和解析

3,832 阅读4分钟

什么是token

简单来说token就是前后端交互的一种令牌,现在多数项目是前后端分离,无状态的

那么后端的接口不能随便让知道这个接口的前端都能访问到

那么token机制就生成了,前端用户登录成功后,后端生成一个令牌token,返回给前端

token一般是一串随机的字符串,后续这个串传到后端,后端能通过这个串去获取这个串保存的用户信息

比如token里面封装了用户的id,和用户的姓名,那么有个接口需要根据用户id查询这个用户的详细信息

没有token的时候你的接口是不是需要传用户id到后端,这种敏感信息用户id是不是就暴露在接口了

如果有token机制,你只需传一串token这个是一串随机字符传很安全,只有传到后端,后端去通过token获取到用户id直接查询,返回给前端

一般token都有过期时间,一但token过期就验证失败使用户重新登录获取新的token

后端怎么生成token

用redis封装token

上面说的这个token是不是用redis很好实现,当用户登录完成,将用户的id加密成一串字符当做redis的key

把用户的信息一般是一个json串,json串里面有用户的详细信息 用户id 用户姓名等用户信息

再加一个过期时间,这个key返回给前端,不就是一个token

当前端把这个key(token传到后端)后端通过这个key去redis就能获取用户的全部信息

jwt

JWT:Json Web Token,是基于Json的一个公开规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息,他的两大使用场景是:认证和数据交换

今天主要是快速的写一个jwt的工具类达到生成token和通过token获取用户信息

开整:

  • 一个JWT实际上就是一个字符串,它由三部分组成,头部载荷签名
  • 生成好的token是三段字符,通过.号连接的如下
token:eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImxlaXhoYW9ob25nIiwidXNlcklkIjoiMTIzNDU1IiwiaWF0IjoxNjYxNjA1Njk5LCJuYmYiOjE2NjE2MDU2OTksImV4cCI6MTY2MTcwNTY5OX0.7vNQpzYhuTXP9MM4nG4tVwIld-5QFY7CG8jj9YiUPzE

代码实现

  • 需要下载的jwt的maven依赖如下:
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
</dependency>
  • 建token的实体,包含token,有效时间 过期时间
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class Token {
    /**
     * token
     */
    private String token;
    /**
     * 有效时间:单位:秒
     */
    private Long expire;
    /**
     * 过期时间
     */
    private LocalDateTime expiration;

    public Token(String token, Long expire, LocalDateTime expiration) {
        this.token = token;
        this.expire = expire;
        this.expiration = expiration;
    }

}
  • 生成jwt并把参数封装到token实体返回

/**
 * 功能简述:
 *
 * @author leixiaohong
 * @date 8/27/22 8:11 PM
 */
@Slf4j
public class JwtUtils {


    /**
     * JWT token 签名
     * 签名密钥长度至少32位
     */
    private static final String JWT_SIGN_KEY = "leixiaohong_jwt_token_json_sign_key";

    private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8));


    /***
     * @Description: 创建令牌
     * @Param:
     * @return: com.tapque.mmo.api.util.Token
     * @Author: leixiaohong
     * @Date: 8/27/22
     */
    public static Token createJwt(Map<String, String> user, long expire) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        //生成签名密钥
        byte[] apiKeySecretBytes = Base64.getDecoder().decode(BASE64_SECURITY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        //添加JWT的类
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken");

        //设置参数到jwt
        user.forEach(builder::claim);

        //Token过期时间
        long expMillis = nowMillis + expire * 1000;
        Date exp = new Date(expMillis);
        builder.setIssuedAt(now).setNotBefore(now).setExpiration(exp).signWith(signingKey, signatureAlgorithm);

        //组装Token信息
        Token tokenInfo = new Token();
        tokenInfo.setToken(builder.compact());
        tokenInfo.setExpire(expire);
        tokenInfo.setExpiration(localDateTime(exp));
        return tokenInfo;
    }

    /**
     * Date转换为LocalDateTime
     *
     * @param date 日期
     */
    public static LocalDateTime localDateTime(Date date) {
        if (date == null) {
            return LocalDateTime.now();
        }
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDateTime();
    }

}
  • 获取Claims

/**
 * 功能简述:
 *
 * @author leixiaohong
 * @date 8/27/22 8:11 PM
 */
@Slf4j
public class JwtUtils {


     /**
     * JWT token 签名
     * 签名密钥长度至少32位
     */
    private static final String JWT_SIGN_KEY = "leixiaohong_jwt_token_json_sign_key";

    private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(JWT_SIGN_KEY.getBytes(StandardCharsets.UTF_8));

    /***
     * @Description: 获取Claims
     * @Param:
     * @return: io.jsonwebtoken.Claims
     * @Author: leixiaohong
     * @Date: 8/27/22
     */
    public static Claims getClaims(String token, long allowedClockSkewSeconds) {
        if (StrUtil.isEmpty(token)) {
            return null;
        }

        return parseJwt(token, allowedClockSkewSeconds);
    }

    /***
     * @Description: 解析jwt
     * @Param:
     * @return: io.jsonwebtoken.Claims
     * @Author: leixiaohong
     * @Date: 8/27/22
     */
    public static Claims parseJwt(String jsonWebToken, long allowedClockSkewSeconds) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY))
                    .setAllowedClockSkewSeconds(allowedClockSkewSeconds)
                    .build()
                    .parseClaimsJws(jsonWebToken)
                    .getBody();
        } catch (ExpiredJwtException ex) {
            log.error("token过期", ex);
            //过期
            //抛异常 让系统捕获到返回到前端
        } catch (SignatureException ex) {
            log.error("签名错误", ex);
            //签名错误
            //抛异常 让系统捕获到返回到前端
        } catch (IllegalArgumentException ex) {
            log.error("token为空", ex);
            //token 为空
            //抛异常 让系统捕获到返回到前端
        } catch (Exception e) {
            log.error("解析token异常", e);
            //抛异常 让系统捕获到返回到前端
        }
        return null;
    }
}

说明:如果获取不到需要封装成异常返回给前端

  • 验证

生成的token接受的参数是个map,我们测试封装的key为userId和userName,值分别是123455和leixiaohong

如果能生成的token,并通过token能获取userId是123455和userName是leixiaohong就说明验证成功

public static void main(String[] args) {
    Map<String, String> param = MapUtil.newHashMap();
    param.put("userId", "123455");
    param.put("userName", "leixiaohong");

    Token token = JwtUtils.createJwt(param, 100000L);

    System.out.println("token:" + token.getToken());
    System.out.println("token的有效期:" + token.getExpire());
    System.out.println("token过期时间:" + token.getExpiration());


    Claims claims = JwtUtils.getClaims(token.getToken(), 60L);


    System.out.println("从token中获取的userId:" +  Convert.toStr(claims.get("userId")));
    System.out.println("从token中获取userName:" +  Convert.toStr(claims.get("userName")));
}
  • 结果
token:eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImxlaXhpYW9ob25nIiwidXNlcklkIjoiMTIzNDU1IiwiaWF0IjoxNjYxNjA4NTQ4LCJuYmYiOjE2NjE2MDg1NDgsImV4cCI6MTY2MTcwODU0OH0.Dm7xRdvMQ0vI5NEPTtPQ4XEAvIikC7hNYeU3bTl5iXM
token的有效期:100000
token过期时间:2022-08-29T01:42:28.216
从token中获取的userId:123455
从token中获取userName:leixiaohong
  • 工具类代码可以直接使用 完