先记住这个答案
JWT的Header声明算法与类型,Payload存放用户和元数据声明,Signature由前两段和密钥生成。服务端验签确保令牌完整,然后检查exp未过期、nbf已生效、iss匹配可信签发方、aud包含当前服务标识。任一步骤失败都拒绝令牌并返回401。
- Header声明算法,Payload含声明,Signature保完整
- 验签必须用约定算法,不能信任Header的alg
- exp、iss、aud必须按精确规则校验并处理缺失
验签与声明校验的执行顺序
JWT把Header和Payload分别Base64URL编码,以点连接,然后按alg指定的算法生成签名。服务端收到令牌后,用自己持有的密钥或公钥对同样内容重算签名,并与传来的Signature比较。若不一致,说明内容被改动,立即拒绝。
签名通过后进入声明校验。exp要求当前时间早于声明值,nbf要求不早于生效时刻,iss需要精确匹配可信签发方,aud为字符串时须与自身标识相同,为数组时须包含自身。全部通过才放行请求。
跨服务调用时的声明不一致实例
某系统由认证服务、用户服务、订单服务构成。订单服务使用密码学密钥验签,并配置iss为'auth.example.com',aud为'order-api'。当用户携带已过期的令牌调用,即使签名正确,exp校验失败,接口返回401。另一个场景中令牌aud为'user-api',未过期但aud不匹配,同样拒绝。
考虑带nbf的令牌:签发于10:00,exp为10:30,nbf为10:00:05。服务端10:00:03收到时,令牌未生效。若服务端采用30秒时钟偏差容忍,则可能通过,但容差过大会增加令牌重用时间,通常仅允许几十秒的系统时钟漂移。
声明缺失与算法信任的失效条件
缺少exp的令牌应被默认拒绝,因为没有可验证的生命周期。同样,iss或aud缺失时,除非显式信任,否则不能放行。宽松匹配允许跨服务使用同一个令牌,破坏最小权限边界。
服务端若信任Header中的alg,攻击者可设为'none'并砍掉签名,或改成RS256后自己签名。必须固定允许算法列表,并确保私钥不泄露,才能让验签有意义。
容易答错的地方
- Payload经过Base64编码即安全
- Base64URL是编码而非加密,任何人解码都能看到。将密码或身份证号放入Payload等于明文泄露,应只放公开可读的必需声明,或用JWE加密内容。
- 只要Signature正确就信任所有声明
- 签名只保证内容未被篡改,不保证声明本身合法。比如过期时间可以正确签名但仍超出允许范围,服务端必须单独校验exp等业务声明,不能仅检查签名。
面试官还会怎么问?
如果JWT没有exp声明,服务端应该怎么处理?
默认应拒收,因为无法控制令牌生命周期。若要支持无过期令牌,必须显式放行并提供服务端侧的撤销或黑名单,但风险极高,通常只用于内部API间调用。
使用RS256验签时,如何获取公钥并防止混淆?
应通过HTTPS访问可信的JWKS端点,按kid字段获取对应公钥。同时需配置允许的iss列表,避免攻击者自建签发服务并替换公钥,导致验签被绕过。
aud的校验规则是什么?
aud表示令牌的接收方。若为字符串,服务端应精确匹配自身标识;若为数组,只要包含服务端标识即可通过。宽松匹配容易导致令牌在互信服务间误用,需用精确值比较。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。