JWT(json web token)中的ExpiredJwtException
1. 问题重现
原本是调用jwtUtil(jwt的工具类),传入一个token,判断是否过期,然而却莫名其妙得抛异常了,而业务中还需要根据是否过期进行后续逻辑! 异常如下:
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2020-07-29T14:48:14Z. Current time: 2020-07-29T14:48:50Z, a difference of 36843 milliseconds. Allowed clock skew: 0 milliseconds. at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:385) at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481) at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541) at com.smart.util.JwtUtil.parseJwt(JwtUtil.java:63) at com.smart.util.JwtUtil.isTokenExpired(JwtUtil.java:93)
2. 问题追踪
根据报错堆栈信息找到了DefaultJwtParser类中,找到了问题的原因
boolean allowSkew = this.allowedClockSkewMillis > 0L; if (claims != null) { Date now = this.clock.now(); long nowTime = now.getTime(); Date exp = claims.getExpiration(); String nbfVal; SimpleDateFormat sdf; if (exp != null) { long maxTime = nowTime - this.allowedClockSkewMillis; Date max = allowSkew ? new Date(maxTime) : now; if (max.after(exp)) { sdf = new SimpleDateFormat("yyyy-MM-ddTHH:mm:ssZ"); String expVal = sdf.format(exp); nbfVal = sdf.format(now); long differenceMillis = maxTime - exp.getTime(); String msg = "JWT expired at " + expVal + ". Current time: " + nbfVal + ", a difference of " + differenceMillis + " milliseconds. Allowed clock skew: " + this.allowedClockSkewMillis + " milliseconds."; throw new ExpiredJwtException((Header)header, claims, msg); } }
看到结尾的throw new ExpiredJwtException,我相信就找到了问题的关键,原来在在解析token并发现这个token已经过期了,它作出的反应是直接抛异常,除了msg信息,还有claims和header信息; 回到我们的工具类中的解析jwt的方法:
public Claims parseJwt(String token){ Claims claims = Jwts.parser() .setSigningKey(signKey) // 设置标识名 .parseClaimsJws(token) //解析token .getBody(); return claims; }
改为:
//不管是否过期,都返回claims对象 public Claims parseJwt(String token){ Claims claims; try { claims = Jwts.parser() .setSigningKey(signKey) // 设置标识名 .parseClaimsJws(token) //解析token .getBody(); } catch (ExpiredJwtException e) { claims = e.getClaims(); } return claims; }
可以从异常中找到这个过期的claim对象信息; 判断token是否过期的方法我也相应修改了,如下:
public Boolean isTokenExpired(String token) { //不管是否过期,都返回claims对象 Claims claims = this.parseJwt(token); Date expiration = claims.getExpiration(); //和当前时间进行对比来判断是否过期 return new Date(System.currentTimeMillis()).after(expiration); }
3. 总结
多多看看错误的堆栈信息,十分有利于我们发现和解决问题!