{"record":{"id":"9d643c07711418bf","repo":"jeecgboot/JeecgBoot","slug":"token-9d643c","errorCode":null,"errorMessage":"Token失效，请重新登录!","messagePattern":"Token失效，请重新登录!","errorType":"exception","errorClass":"JeecgBoot401Exception","httpStatus":401,"severity":"error","filePath":"jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java","lineNumber":126,"sourceCode":"        if (username == null) {\n            throw new JeecgBoot401Exception(\"token非法无效!\");\n        }\n\n        // 查询用户信息\n        LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);\n        //LoginUser user = commonApi.getUserByName(username);\n        if (user == null) {\n            throw new JeecgBoot401Exception(\"用户不存在!\");\n        }\n        // 判断用户状态\n        if (user.getStatus() != 1) {\n            throw new JeecgBoot401Exception(\"账号已被锁定,请联系管理员!\");\n        }\n        // 校验token是否超时失效 & 或者账号密码是否错误\n        if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {\n            // 用户登录Token过期提示信息\n            String userLoginTokenErrorMsg = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN_ERROR_MSG + token));\n            throw new JeecgBoot401Exception(oConvertUtils.isEmpty(userLoginTokenErrorMsg)? CommonConstant.TOKEN_IS_INVALID_MSG: userLoginTokenErrorMsg);\n        }\n        return true;\n    }\n\n    /**\n     * 刷新token（保证用户在线操作不掉线）\n     * @param token\n     * @param userName\n     * @param passWord\n     * @param redisUtil\n     * @return\n     */\n    private static boolean jwtTokenRefresh(String token, String userName, String passWord, RedisUtil redisUtil) {\n        String cacheToken = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));\n        if (oConvertUtils.isNotEmpty(cacheToken)) {\n            // 校验token有效性\n            if (!JwtUtil.verify(cacheToken, userName, passWord)) {\n                // 从token中解析客户端类型，保持续期时使用相同的客户端类型","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java#L108-L144","documentation":"Thrown by TokenUtils.verifyToken when jwtTokenRefresh() returns false — the token's TTL has expired, or the password in the DB no longer matches the password hash baked into the token claim. The actual message comes from a Redis error-msg cache (PREFIX_USER_TOKEN_ERROR_MSG+token) or falls back to CommonConstant.TOKEN_IS_INVALID_MSG. Returns HTTP 401.","triggerScenarios":"User idle longer than the token TTL (jeecg.token.expire, default ~30 min) and the sliding-refresh window also expired; the user changed their password so the token's embedded password hash no longer matches; multiple logins caused the single-session token to be invalidated.","commonSituations":"Leaving the app open overnight; password change on another device; token sliding-refresh window (jwtTokenRefresh's allowed skew) exhausted; clock drift between client and server.","solutions":["Re-authenticate: redirect to login and obtain a fresh token.","If single-device login is enforced, the prior session token is intentionally killed — this is expected, not a bug.","After a password change, all prior tokens are by design invalid; log in again with the new password.","To extend sessions, increase jeecg.token.expire / the refresh window in application.yml rather than disabling the check."],"exampleFix":"// before\n// token expired after 30 min idle -> 401 'Token失效，请重新登录!'\n\n// after (frontend interceptor)\nif (msg.includes('Token失效') || status === 401) {\n  store.dispatch('Logout').then(() => router.push('/user/login'));\n}","handlingStrategy":"retry","validationCode":"// client-side: refresh window before the call\ndecodeJwt(token).exp * 1000 - Date.now() < 60000 -> call /sys/refresh or re-login","typeGuard":"public static boolean tokenLikelyFresh(String token, long ttlMs){\n    try { return Jwts.parser().parseClaimsJws(token).getBody().getExpiration().getTime() - System.currentTimeMillis() > ttlMs; }\n    catch (Exception e) { return false; }\n}","tryCatchPattern":"try { TokenUtils.verifyToken(token, ...); }\ncatch (JeecgBoot401Exception e) { if (expired(e)) silentRefresh().orElseGet(this::relogin); }","preventionTips":["Implement a silent token refresh before expiry.","After password change, invalidate and re-login everywhere.","Size the token TTL to match user workflow, not the security boundary alone."],"tags":["authentication","jwt","session","token-expiry","jeecg-boot","http-401"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}