dromara/Sa-Token · error · SaJwtException

30203

30203

Error message

jwt loginType 无效:

What it means

Thrown by SaJwtTemplate.parseToken when the token's payload loginType claim does not equal the loginType argument passed to parseToken. Sa-token embeds the account system (loginType, e.g. 'login' or a custom StpLogic type) in each JWT; code 30203 marks a mismatch.

Source

Thrown at sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtTemplate.java:198

    	
    	// 解析 
    	JWT jwt;
    	try {
    		jwt = JWT.of(token);
		} catch (JWTException | JSONException e) {
    		throw new SaJwtException("jwt 解析失败:" + token, e).setCode(SaJwtErrorCode.CODE_30201);
		}
    	JSONObject payloads = jwt.getPayloads();
    	
    	// 校验 Token 签名
		boolean verify = jwt.setSigner(createSigner(keyt)).verify();
    	if( ! verify) {
    		throw new SaJwtException("jwt 签名无效:" + token).setCode(SaJwtErrorCode.CODE_30202);
    	}

    	// 校验 loginType 
    	if( ! Objects.equals(loginType, payloads.getStr(LOGIN_TYPE))) {
    		throw new SaJwtException("jwt loginType 无效:" + token).setCode(SaJwtErrorCode.CODE_30203);
    	}
    	
    	// 校验 Token 有效期
    	if(isCheckTimeout) {
    		Long effTime = payloads.getLong(EFF, 0L);
        	if(effTime != NEVER_EXPIRE) {
        		if(effTime == null || effTime < System.currentTimeMillis()) {
        			throw new SaJwtException("jwt 已过期:" + token).setCode(SaJwtErrorCode.CODE_30204);
        		}
        	}
    	}
    	
        // 返回 
        return jwt;
    }

    /**
     * 获取 jwt 数据载荷 (校验 sign、loginType、timeout) 

View on GitHub (pinned to ac2c7f6e94)

Solutions

  1. Make the loginType used in parseToken match the StpLogic that issued the token (use stpLogic.getLoginType(), not a literal)
  2. If tokens are shared across services, standardize one loginType for issuance and verification
  3. Inspect the token's payload (decode the middle segment) to see which loginType claim it carries

Example fix

// before
// token was issued by StpUtil (loginType="login")
stpLogicUser.getStpLogicJwt().parseToken(token, "user", secret, true); // 30203

// after
// verify with the loginType that issued the token
stpLogicUser.getStpLogicJwt().parseToken(token, "login", secret, true);
// or generically: parseToken(token, issuingStpLogic.getLoginType(), secret, true);
Defensive patterns

Strategy: validation

Validate before calling

// verify with the issuing logic's own loginType
String loginType = issuingStpLogic.getLoginType();
payloads = jwtTemplate.parseToken(token, loginType, secret, false).getPayloads();
if (!Objects.equals(expectedType, payloads.getStr("loginType"))) { ... }

Try / catch

catch (SaJwtException e) { if (e.getCode() == SaJwtErrorCode.CODE_30203) { /* token from wrong account system: 401 */ } }

Prevention

When it happens

Trigger: Parsing a token issued for StpLogic 'login' while passing loginType 'user' (or vice versa) — e.g. a multi-account-type app where the wrong StpLogic or hard-coded loginType is used during verification.

Common situations: Custom StpLogic with a new loginType but the token was created under the default type; copying verification code from another account-type module and forgetting to change the loginType string; frontend sending a token from app A to app B which share a secret but not a loginType.

Related errors


AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14). Data as JSON: /api/errors/17520c8a6dfbdd01. Report an issue: GitHub.