dromara/Sa-Token · error · SaJwtException
30201
30201
Error message
jwt 解析失败:
What it means
Thrown by SaJwtTemplate.parseToken when JWT.of(token) raises JWTException or JSONException — the string is not structurally a valid JWT (header.payload.signature, base64 JSON parts). Code 30201 marks a jwt parse failure; the original exception is chained.
Source
Thrown at sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/SaJwtTemplate.java:186
*/
public JWT parseToken(String token, String loginType, String keyt, boolean isCheckTimeout) {
// 秘钥不可以为空
if(SaFoxUtil.isEmpty(keyt)) {
throw new SaJwtException("请配置 jwt 秘钥");
}
// 如果token为null
if(token == null) {
throw new SaJwtException("jwt 字符串不可为空");
}
// 解析
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) {View on GitHub (pinned to ac2c7f6e94)
Solutions
- Log/inspect the exact token string received (length, dots, base64 charset) and compare with the issued token
- Strip the 'Bearer ' prefix before parsing if you extract the header yourself
- Make the client send the token exactly as issued, without re-encoding or truncation
Example fix
// before
String raw = request.getHeader("Authorization"); // "Bearer eyJ..."
parseToken(raw, loginType, secret, true); // 'Bearer ...' is not a JWT -> 30201
// after
String raw = request.getHeader("Authorization");
String token = StrUtil.removePrefix(raw, "Bearer ").trim();
parseToken(token, loginType, secret, true); Defensive patterns
Strategy: validation
Validate before calling
String t = StrUtil.removePrefix(raw, "Bearer ").trim();
boolean looksLikeJwt = t.split("\\.").length == 3;
if (!looksLikeJwt) return unauthorized("malformed token"); Try / catch
catch (SaJwtException e) { if (e.getCode() == SaJwtErrorCode.CODE_30201) { /* 401 malformed; log token length not full value */ } } Prevention
- Strip 'Bearer ' prefix at the extraction boundary exactly once
- Never re-encode or rewrite tokens client-side; treat them as opaque strings
When it happens
Trigger: Passing a malformed token: not three dot-separated segments, invalid base64, or payload that is not JSON. Happens before signature verification, so even a correctly-signed blob with a corrupted character fails here.
Common situations: Client truncates or wraps the token (log copy-paste with line breaks); token URL-encoded twice so '.' handling breaks; opaque session token sent to a JWT-mode endpoint; token prefixed with 'Bearer ' but not stripped; hand-crafted test tokens.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/16fd99f84d5ff874.
Report an issue: GitHub.