• JWT如何解析过期的token中的信息


    一、问题背景

      最近搭建springcloud的项目,项目采取了Jwt + spring security 来进行登录验证,Jwt token 锁定用户的失效时间,但是由于 jwt token特性导致token失效时间无法刷新,所以必须新创建一个token令牌,用来代替之前已失效token。

      (token失效时间无法刷新的原因是由于jwt创建token是根据jwt保存的相关信息来计算的,过期时间是其中的一个计算维度,所以一旦过期时间改了,那么生成的token值也就变了。)

      之后为了解决这个问题,结合了redis,将token值保存到redis中,用户操作后刷新redis的有效时间,这样如果jwt token失效了,再检查 redis 中保存token的key是否失效,如果没有失效,那么就重新创建jwt token ,失效了,就重新登录。

      我用一个过期token去调用接口,返回请求未授权。我查看token拦截的方法,发现是token解析异常,返回null导致接口返回未授权。token解析的最终处理都是parseJWT(jsonWebToken)这个方法,代码如下:

    1. public static Claims parseJWT(String jsonWebToken) {
    2. try {
    3. return Jwts.parser().setSigningKey(Base64.getDecoder().decode(getBase64Security())).parseClaimsJws(jsonWebToken).getBody();
    4. } catch (Exception var2) {
    5. return null;
    6. }
    7. }

    二、问题原因

    根据报错堆栈信息找到了DefaultJwtParser类中,找到了问题的原因。

    传入过期token,debug调试发现是在DefaultJwtParser的parse(String jwt)方法中返回了ExpiredJwtException 过期异常。部分代码如下

    1. boolean allowSkew = this.allowedClockSkewMillis > 0L;
    2. if (claims != null) {
    3. Date now = this.clock.now();
    4. long nowTime = now.getTime();
    5. Date exp = claims.getExpiration();
    6. String nbfVal;
    7. SimpleDateFormat sdf;
    8. if (exp != null) {
    9. long maxTime = nowTime - this.allowedClockSkewMillis;
    10. Date max = allowSkew ? new Date(maxTime) : now;
    11. if (max.after(exp)) {
    12. sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    13. String expVal = sdf.format(exp);
    14. nbfVal = sdf.format(now);
    15. long differenceMillis = maxTime - exp.getTime();
    16. String msg = "JWT expired at " + expVal + ". Current time: " + nbfVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds.";
    17. throw new ExpiredJwtException((Header)header, claims, msg);
    18. }
    19. }
    20. Date nbf = claims.getNotBefore();
    21. if (nbf != null) {
    22. long minTime = nowTime + this.allowedClockSkewMillis;
    23. Date min = allowSkew ? new Date(minTime) : now;
    24. if (min.before(nbf)) {
    25. sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    26. nbfVal = sdf.format(nbf);
    27. String nowVal = sdf.format(now);
    28. long differenceMillis = nbf.getTime() - minTime;
    29. String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds.";
    30. throw new PrematureJwtException((Header)header, claims, msg);
    31. }
    32. }
    33. this.validateExpectedClaims((Header)header, claims);
    34. }

    看到结尾的throw new ExpiredJwtException,我相信就找到了问题的关键,原来在在解析token并发现这个token已经过期了,它作出的反应是直接抛异常。

    异常定义的构造方法中除了msg信息,还有claims和header信息。

    检查claims发现,在异常之前token其实已经解析完毕。

    这样也就代表着,抛出的这个异常 ExpiredJwtException 中有一个参数 claims 就是解析后的token,那么本次这个问题也就解决了。

      catch ExpiredJwtException 异常后,直接从异常中获取解析的数据即可。

    三、方法处理

    修改原来的parseJWT(jsonWebToken),代码如下:

    1. public static Claims parseJWT(String jsonWebToken) {
    2. try {
    3. return Jwts.parser().setSigningKey(Base64.getDecoder().decode(getBase64Security())).parseClaimsJws(jsonWebToken).getBody();
    4. } catch (ExpiredJwtException var) {
    5. return var.getClaims();
    6. } catch (PrematureJwtException var1) {
    7. return var1.getClaims();
    8. } catch (Exception var2) {
    9. return null;
    10. }
    11. }

     

    ExpiredJwtException 异常是超过token过期时间异常
    PrematureJwtException 异常是早于token生效时间异议

    附带判断token是否过期的方法代码:

    1. public Boolean isTokenExpired(String token) {
    2. //不管是否过期,都返回claims对象
    3. Claims claims = parseJWT(token);
    4. Date expiration = claims.getExpiration();
    5. //和当前时间进行对比来判断是否过期
    6. return new Date(System.currentTimeMillis()).after(expiration);
    7. }

    最终测试,可以从过期的token中获取用户信息了,接口也不拦截过期token了(因为之间的拦截器代码是根据token能不能解析用户信息去拦截的)。

    相关文章:

    SpirngBoot设置自定义注解@NoToken去除部分接口的token验证_洛阳泰山的博客-CSDN博客

  • 相关阅读:
    docker容器设置简单启动命令,不退出
    VR航天科普体验馆VR航空馆规划遨游太空感受其中乐趣
    【算法篇】刷了两道大厂面试题,含泪 ”重学数组“
    软件测试---边界值分析(功能测试)
    【今日话题】如何看待Unity收费一事,对标中小公司的从业者的该如何做
    百度飞桨(PaddlePaddle) - PaddleHub OCR 文字识别简单使用
    .Net Redis 实现分布式锁以及实现Gzip数据压缩提升性能
    js-原生对象
    【Leetcode】 406. 根据身高重建队列
    训练数据有缺陷?TrustAI来帮你!
  • 原文地址:https://blog.csdn.net/weixin_40986713/article/details/127610288