jeecgboot/JeecgBoot · error · JeecgBoot401Exception
token非法无效!
Error message
token非法无效!
What it means
Thrown by TokenUtils.verifyToken when JwtUtil.getUsername(token) returns null — meaning the JWT either cannot be parsed, the signature is invalid, or the token has no 'username' claim. This is the signature/structure validation step; it precedes the Redis user lookup. Returns HTTP 401.
Source
Thrown at jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java:109
*/
public static boolean verifyToken(HttpServletRequest request, CommonAPI commonApi, RedisUtil redisUtil) {
log.debug(" -- url --" + request.getRequestURL());
String token = getTokenByRequest(request);
return TokenUtils.verifyToken(token, commonApi, redisUtil);
}
/**
* 验证Token
*/
public static boolean verifyToken(String token, CommonAPI commonApi, RedisUtil redisUtil) {
if (StringUtils.isBlank(token)) {
throw new JeecgBoot401Exception("token不能为空!");
}
// 解密获得username,用于和数据库进行对比
String username = JwtUtil.getUsername(token);
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);
}View on GitHub (pinned to 96fb33f5ec)
Solutions
- Force a fresh login so a new token is minted with the current secret and claims.
- Verify the JWT signing secret is identical across all instances/services that issue or consume tokens (jeecg.jwt.secret / jeecg.signer).
- Confirm the token is a real 3-part JWT (header.payload.signature) and not 'null' or 'Bearer null'.
- Check that the username claim key matches what JwtUtil.getUsername reads (default 'username').
Example fix
// before
String token = "null"; // stored literally
TokenUtils.verifyToken(token, ...); // throws token非法无效
// after
if ("null".equals(token) || token == null) {
redirect to login;
} Defensive patterns
Strategy: try-catch
Validate before calling
try {
Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
} catch (JwtException e) {
// token invalid -> force re-login
} Type guard
public static boolean isPlausibleJwt(String t){
return t != null && t.chars().filter(c -> c == '.').count() == 2;
} Try / catch
try { TokenUtils.verifyToken(token, commonApi, redisUtil); }
catch (JeecgBoot401Exception e) { if (e.getMessage().contains("非法")) forceRelogin(); } Prevention
- Keep the JWT signing secret identical across all environments that consume tokens.
- Reject 'null'/'undefined' literal tokens client-side.
- Rotate secrets with a token-invalidation strategy.
When it happens
Trigger: Sending a malformed or truncated token string, a token signed with a different secret (e.g. dev secret used in prod), a tampered token whose signature no longer matches, or a non-JWT value (a plain session id or 'null' literal) placed in the header.
Common situations: The jwt secret (jeecg.signer or signature.secret) was changed/rotated between releases so old tokens fail; an environment variable for the secret was not set so a different default was used; a token from another JeecgBoot tenant; clock skew causing parsing edge cases.
Related errors
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/7140f2dd20c54d80.
Report an issue: GitHub.