jeecgboot/JeecgBoot · error · JeecgBoot401Exception
Token失效,请重新登录!
Error message
Token失效,请重新登录!
What it means
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.
Source
Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java:126
if (username == null) {
throw new JeecgBoot401Exception("token非法无效!");
}
// 查询用户信息
LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);
//LoginUser user = commonApi.getUserByName(username);
if (user == null) {
throw new JeecgBoot401Exception("用户不存在!");
}
// 判断用户状态
if (user.getStatus() != 1) {
throw new JeecgBoot401Exception("账号已被锁定,请联系管理员!");
}
// 校验token是否超时失效 & 或者账号密码是否错误
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
// 用户登录Token过期提示信息
String userLoginTokenErrorMsg = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN_ERROR_MSG + token));
throw new JeecgBoot401Exception(oConvertUtils.isEmpty(userLoginTokenErrorMsg)? CommonConstant.TOKEN_IS_INVALID_MSG: userLoginTokenErrorMsg);
}
return true;
}
/**
* 刷新token(保证用户在线操作不掉线)
* @param token
* @param userName
* @param passWord
* @param redisUtil
* @return
*/
private static boolean jwtTokenRefresh(String token, String userName, String passWord, RedisUtil redisUtil) {
String cacheToken = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
if (oConvertUtils.isNotEmpty(cacheToken)) {
// 校验token有效性
if (!JwtUtil.verify(cacheToken, userName, passWord)) {
// 从token中解析客户端类型,保持续期时使用相同的客户端类型View on GitHub (pinned to 96fb33f5ec)
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.
Example fix
// before
// token expired after 30 min idle -> 401 'Token失效,请重新登录!'
// after (frontend interceptor)
if (msg.includes('Token失效') || status === 401) {
store.dispatch('Logout').then(() => router.push('/user/login'));
} Defensive patterns
Strategy: retry
Validate before calling
// client-side: refresh window before the call decodeJwt(token).exp * 1000 - Date.now() < 60000 -> call /sys/refresh or re-login
Type guard
public static boolean tokenLikelyFresh(String token, long ttlMs){
try { return Jwts.parser().parseClaimsJws(token).getBody().getExpiration().getTime() - System.currentTimeMillis() > ttlMs; }
catch (Exception e) { return false; }
} Try / catch
try { TokenUtils.verifyToken(token, ...); }
catch (JeecgBoot401Exception e) { if (expired(e)) silentRefresh().orElseGet(this::relogin); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/9d643c07711418bf.
Report an issue: GitHub.