JWT生成token时,设置过期时间通常如下:
public static final int SESSION_TIME_OUT = 3600 * 24 *30;
/**
* 私钥加密token
*
* @param uid 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位分钟
* @return JWT
*/
public static String buildJwtRsaToken(Long uid, PrivateKey privateKey, int expire) {
//签发时间. 注意:尽量比当前时间稍微提前一点,防止验证时间相隔太短,导致验证不通过
long startTime = System.currentTimeMillis() - 60;
//过期时间. 在签发时间的基础上,加上一个时长
Date end = new Date(startTime + SessionConstants.SESSION_TIME_OUT * 1000); //设置过期
Date start = new Date(startTime); //设开始
return Jwts.builder()
.claim(JWT_PAYLOAD_SID, createJTI())
.setId(String.valueOf(uid))
.setExpiration(end)
.setIssuedAt(start)
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
执行生成token
@Test
public void testGenToken() throws Exception {
String base = getBase();
//获取当前认证的对象
UserDTO userDTO = new UserDTO();
userDTO.setUsername("username");
userDTO.setUserId(10000L);
//获取生成token的私钥
PrivateKey privateKey = RsaUtils.getPrivateKey(base + privateKeyFileName);
//定义token过期时间为一天
int expireTime = 24 * 60;
//生成token
String token = JwtServiceImpl.buildJwtRsaToken(userDTO.getUserId(), privateKey, expireTime);
System.out.println("token = " + token);
}
运行结果:
token = eyJhbGciOiJSUzI1NiJ9.eyJzaWQiOiJOR05sTW1WaE1UZ3RZMll3WXkwME9HTTRMVGczTm1ZdE1USm1PVGswTmpObVl6QTAiLCJqdGkiOiIxMDAwMCIsImV4cCI6MTcyNDQ4NDQyMywiaWF0IjoxNzI2MTg3MzkwfQ.LF7H0sI-PU3DJhqOYCVgwd1xvzriCMOTckaYdqoq2Zrsnatb5cRStNa8H0TnDa0Vrv3tJLXNfYx7omzdvRo1PK9zP7_es6jnuE8DtJhK5YMWkfAcehJgO1zDOwOWbJx9Nr5vzNTMRT6B-bLSFJbTdk18c6ka68oowq7qoozVp5GbXYayrZpllCsUyGU3xMKOqCmb487au43yNx-M7ZmUIyzXuuXGZPDoq_6QSTYRzidBwbGz8XeMMjb6o-jwVoHvwtInYQ4YMEBDFsZlaglrhdDc2NLHPpGqHtdHMEAD--lopB0nqimv6W3wUasT2xcKiWBOsVxf6ajjukfmQAg5Zw
Process finished with exit code 0
看似一切正常
校验token时就会出问题
private String getOneToken() throws Exception {
String base = getBase();
//获取当前认证的对象
UserDTO userDTO = new UserDTO();
userDTO.setUsername("username");
userDTO.setUserId(10000L);
//获取生成token的私钥
PrivateKey privateKey = RsaUtils.getPrivateKey(base + privateKeyFileName);
//定义token过期时间为一天
int expireTime = 24 * 60;
//生成token
String token = JwtServiceImpl.buildJwtRsaToken(userDTO.getUserId(), privateKey, expireTime);
System.out.println(token);
return token;
}
@Test
public void testCheckToken() throws Exception {
//如果token格式正确,就验证token
String token = getOneToken();
//获取验证token的公钥
String base = getBase();
PublicKey publicKey = RsaUtils.getPublicKey(base + publicKeyFileName);
Payload<String> payload = JwtServiceImpl.decodeJwtRsaToken(token, publicKey);
String json = JsonUtil.pojoToJson(payload);
System.err.println(json);
}
执行结果如下
eyJhbGciOiJSUzI1NiJ9.eyJzaWQiOiJNV0poWlRKak0yUXRZalJpTUMwMFpXVmxMV0UyTVRjdE9URmxaVFJsTlRVM05UbGwiLCJqdGkiOiIxMDAwMCIsImV4cCI6MTcyNDQ4NDc5MSwiaWF0IjoxNzI2MTg3NzU5fQ.MQZ7tSPzo6L7pI2JYqbm7yroMIGwiBcWNue9hf24XlSN4vN5NB4nRuj59-d5u5cNgww49Rz98g-_P6vU-g2U3aoC7HizZ0_BWMRzsmosMWp9aKqztG5Dh_qiqs3NcF1p8npaJzZY6Oi8io1GT9Fem82H4eh56dItRy7T75mgzdvJjQIRBZyDwyktFzeVOwMHRcMnvAw10gefPb1QDa-QK-oK_XbGtO0dgiPei-kLdNZxY7SXRMgOhUIHCC15_QSjDDyOGOKHJI0l5Z7or7zz06g1edWLr9nMKrIlO2Re6KQ82Tw7pvFRTj-KJlEQZmHmD5L6UficlKktPOu6hpZJ5A
io.jsonwebtoken.ExpiredJwtException: JWT expired at 2024-08-24T07:33:11Z. Current time: 2024-09-13T00:36:00Z, a difference of 1702969788 milliseconds. Allowed clock skew: 0 milliseconds.
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:411)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:513)
at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:573)
at com.crazymaker.springcloud.base.service.impl.JwtServiceImpl.parseJwtRsaToken(JwtServiceImpl.java:158)
at com.crazymaker.springcloud.base.service.impl.JwtServiceImpl.decodeJwtRsaToken(JwtServiceImpl.java:104)
at com.crazymaker.springcloud.test.sso.JwtRsaTest.testCheckToken(JwtRsaTest.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
业务异常
BusinessException(errCode=-1, errMsg=token 验证异常)
at com.crazymaker.springcloud.common.exception.BusinessException$Builder.build(BusinessException.java:61)
at com.crazymaker.springcloud.base.service.impl.JwtServiceImpl.parseJwtRsaToken(JwtServiceImpl.java:162)
at com.crazymaker.springcloud.base.service.impl.JwtServiceImpl.decodeJwtRsaToken(JwtServiceImpl.java:104)
at com.crazymaker.springcloud.test.sso.JwtRsaTest.testCheckToken(JwtRsaTest.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Process finished with exit code -1
看问题的描述:
JWT expired at 2024-08-24T07:33:11Z. Current time: 2024-09-13T00:36:00Z, a difference of 1702969788 milliseconds. Allowed clock skew: 0 milliseconds.
过期时间为2024-08-24T07:33:11,当前时间为2024-09-13T00:36:00,token过期了,但是我明明是获取token后马上执行的解密,那么问题应该在设置过期时间
定位相关代码:
/**
* session 的过期时间 单位 s
*/
public static final int SESSION_TIME_OUT = 3600 * 24 *30;
//签发时间. 注意:尽量比当前时间稍微提前一点,防止验证时间相隔太短,导致验证不通过
long startTime = System.currentTimeMillis() - 6000;
//过期时间. 在签发时间的基础上,加上一个时长
Date end = new Date(startTime + SessionConstants.SESSION_TIME_OUT * 1000); //设置过期
看着没啥问题,过期时间是在当前时间上加的,而且这个时间设置得挺长的。
那么问题是什么啦?
打印这个过期时间
System.out.println(SessionConstants.SESSION_TIME_OUT * 1000);
先问问chatgpt:
他给的回答看不出问题
最后自己执行下:
-1702967296
拿着结果再问问他
原来当你尝试计算2592000 * 1000时,实际的结果2592000000已经超出了int类型的最大值。因此,在Java中,这个计算会导致整数溢出,而溢出的结果是一个负数,即你看到的-1702967296。
问题解决了
总结下:
1.int的大数计算要小心整数溢出的问题
2.不要盲目信任chatgpt,有问题还是需要自己代码执行下,拿着结果再去问问题