F

JWT 解析的 5 个常见报错:签名失效、过期、算法混淆与排查清单5 Common JWT Parsing Errors: Invalid Signature, Expiry, Algorithm Confusion and a Debug Checklist

JWT 看起来就是三段 Base64URL 拼起来的字符串,但实际联调时签名失效、过期、算法不匹配这类报错能耗掉大半天。本文整理我踩过的 5 个高频坑,附一个可直接照着走的排查清单A JWT looks like three Base64URL segments glued together, but in real integrations, invalid signatures, expiry and algorithm mismatches can eat half a day. This article collects five high-frequency pitfalls I've hit, plus a debug checklist you can follow step by step.

JWT 的三段结构与验签本质The three-part structure and what signature verification really does

先把基础说清楚。JWT 由 header、payload、signature 三段组成,前两段是 JSON 经 Base64URL 编码后的结果,任何人都能解码看到内容——所以 JWT 不是加密,只是签名。signature 是用 header 里声明的算法(如 HS256、RS256)对 `header.payload` 这段字符串做签名得到的。Let's get the basics straight first. A JWT consists of three parts: header, payload and signature. The first two are JSON encoded as Base64URL — anyone can decode them and read the content, so JWT is not encryption, only signing. The signature is produced by signing the string `header.payload` with the algorithm declared in the header (e.g., HS256, RS256).

验签的本质是:服务端用自己持有的密钥(对称算法的 secret,或非对称算法的公钥),对收到的 `header.payload` 重新算一次签名,和 token 里带的 signature 做常量时间比较。任何一段被篡改、密钥不对、算法不一致,比较就会失败。理解了这一点,下面五个报错的根因就一目了然。The essence of verification is: the server uses its own key (a secret for symmetric algorithms, or a public key for asymmetric ones) to recompute the signature over the received `header.payload`, then compares it with the signature in the token in constant time. Any tampered segment, wrong key, or algorithm mismatch causes the comparison to fail. Once you understand this, the root causes of the five errors below become obvious.

五个常见报错与真实案例Five common errors and real-world cases

第一个,`invalid signature`(签名无效)。这是最常见也最磨人的。我遇到过一次,前端用 `jsonwebtoken` 库的默认编码生成 token,后端用 Java 的 `jjwt` 验签,两边 secret 看起来一样但就是验不过。最后发现前端的 secret 多了一个尾部换行符——从配置文件读入时没 trim。另一个高频原因是密钥类型搞混:HS256 用的是任意字符串 secret,RS256 用的是 PEM 格式的公钥/私钥,把 RSA 私钥当 HS256 的 secret 传进去,库可能不报错但签名一定对不上。First, `invalid signature`. This is the most common and most frustrating. I once had a case where the frontend generated tokens with the `jsonwebtoken` library's default encoding and the backend verified with Java's `jjwt` — the secrets looked identical but verification always failed. It turned out the frontend's secret had a trailing newline that wasn't trimmed when read from the config file. Another frequent cause is mixing up key types: HS256 uses an arbitrary string secret, while RS256 uses PEM-formatted public/private keys. Passing an RSA private key as an HS256 secret may not throw an error, but the signature will never match.

第二个,`jwt expired`(已过期)。payload 里的 `exp` 字段是 Unix 时间戳(秒),很多人误填成毫秒,导致 token 一签发就"已过期 50 年"。反过来,服务端时钟和签发端不同步(差几分钟),也会出现刚签发就过期的假象。还有一个隐蔽场景:刷新 token 逻辑里,旧 token 的 `exp` 没到但被加入了黑名单,业务层报"过期"其实是被主动吊销,排查时要区分是 `exp` 超时还是黑名单命中。Second, `jwt expired`. The `exp` field in the payload is a Unix timestamp in seconds; many people mistakenly fill in milliseconds, causing the token to be "expired by 50 years" the moment it's issued. Conversely, if the server clock is out of sync with the issuer (off by a few minutes), you can see a phantom "just issued, already expired" situation. There's also a subtle case: in refresh-token logic, an old token's `exp` hasn't arrived but it's been blacklisted — the business layer reports "expired" when it's actually been actively revoked. Distinguish between `exp` timeout and blacklist hits during debugging.

第三个,`algorithm mismatch`(算法不匹配),也叫算法混淆攻击。header 里的 `alg` 字段是 token 自带声明的,如果服务端验签时不校验 `alg` 是否在白名单内,攻击者可以把 RS256 的 token 改成 HS256,然后用服务端的 RSA 公钥当 secret 来签名——因为公钥是公开的,这样就能伪造出能通过验签的 token。修复方式很简单:验签时强制指定期望的算法,绝不信任 token 自带的 `alg`。Third, `algorithm mismatch`, also known as the algorithm confusion attack. The `alg` field in the header is self-declared by the token. If the server doesn't verify that `alg` is within a whitelist, an attacker can change an RS256 token to HS256 and sign it using the server's RSA public key as the secret — since the public key is public, this forges a token that passes verification. The fix is simple: force the expected algorithm during verification, never trust the token's own `alg`.

第四个,`invalid audience` 或 `invalid issuer`(受众/签发者不匹配)。`aud` 和 `iss` 是可选但推荐的字段,多系统共用一个认证中心时尤其容易踩坑:A 系统签发的 token 被拿到 B 系统用,`aud` 对不上就被拒。排查时先把 payload 解码出来看这两个字段的实际值,再和服务端配置对比。Fourth, `invalid audience` or `invalid issuer`. The `aud` and `iss` fields are optional but recommended, and they're especially easy to get wrong when multiple systems share an auth center: a token issued by system A gets used in system B, the `aud` doesn't match, and it's rejected. Decode the payload first to see the actual values of these fields, then compare with the server configuration.

第五个,`signature verification failed` 但 token 看起来没问题——这种情况十有八九是传输过程中被截断或转义了。最典型的是 URL query 参数里传 token,`+`、`/`、`=` 这些字符虽然 JWT 用的是 Base64URL(已经把 `+` 换成 `-`、`/` 换成 `_`、去掉了 `=`),但如果中间有一层代理或网关做了额外的 URL 解码再编码,就可能破坏。建议 token 放 Authorization header,不要放 query。Fifth, `signature verification failed` but the token looks fine — nine times out of ten this means the token was truncated or escaped during transport. The classic case is passing the token in a URL query parameter. Although JWT uses Base64URL (which already swaps `+` to `-`, `/` to `_`, and strips `=`), if an intermediate proxy or gateway does an extra URL decode-then-encode, it can corrupt the token. Put the token in the Authorization header, not the query string.

排查清单与工具A debug checklist and the tools

遇到 JWT 报错时,我固定按这个顺序走:第一步,把 token 粘贴到 JWT 解析器里,看 header 的 `alg`、`typ` 和 payload 的 `exp`、`iat`、`aud`、`iss` 是否符合预期,特别注意 `exp` 是秒还是毫秒。第二步,确认验签端使用的密钥类型和算法与签发端一致——HS256 配 secret,RS256 配公钥,不要混用。第三步,检查服务端时钟是否同步(`date` 命令对比 NTP 时间)。第四步,确认 token 传输路径上没有被截断、转义或额外编码。第五步,如果是多系统,核对 `aud` 和 `iss` 白名单。When I hit a JWT error, I always follow this order: Step one, paste the token into a JWT decoder and check the header's `alg`, `typ` and the payload's `exp`, `iat`, `aud`, `iss` — pay special attention to whether `exp` is in seconds or milliseconds. Step two, confirm the verification side uses the same key type and algorithm as the issuer — HS256 with a secret, RS256 with a public key, never mixed. Step three, check that the server clock is synced (compare with NTP using the `date` command). Step four, confirm the token wasn't truncated, escaped, or re-encoded along the transport path. Step five, for multi-system setups, verify the `aud` and `iss` whitelists.

走完这五步,95% 以上的 JWT 问题都能定位。剩下 5% 通常是库的版本 bug 或自定义 claim 的类型问题,那时候再去翻源码也不迟。本站的 JWT 解析器可以在不联网的情况下解码 header 和 payload,高亮 `exp` 是否过期,方便快速定位问题。Following these five steps locates over 95% of JWT issues. The remaining 5% are usually library version bugs or custom claim type problems, and by then digging into source code is worthwhile. Our JWT decoder can decode the header and payload offline, highlights whether `exp` has passed, and helps you pinpoint problems fast.

← 返回教程列表← Back to all guides