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
- Make the loginType used in parseToken match the StpLogic that issued the token (use stpLogic.getLoginType(), not a literal)
- If tokens are shared across services, standardize one loginType for issuance and verification
- 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
- Derive loginType from the StpLogic constant instead of string literals
- In multi-app SSO, namespace tokens by app and verify the namespace claim explicitly
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.