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

  1. Force a fresh login so a new token is minted with the current secret and claims.
  2. Verify the JWT signing secret is identical across all instances/services that issue or consume tokens (jeecg.jwt.secret / jeecg.signer).
  3. Confirm the token is a real 3-part JWT (header.payload.signature) and not 'null' or 'Bearer null'.
  4. 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

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.