---

title: JWT声明与时限验证(aud/iss/exp/nbf/jti)最佳实践

keywords:

  • JWT
  • aud
  • iss
  • exp
  • nbf
  • jti
  • leeway

description: 通过aud/iss/exp/nbf/jti校验与算法限定、时钟偏移容忍与一次性标识,保障JWT在多环境中的稳健可信。

categories:

  • 文章资讯
  • 技术教程

---

背景与价值

JWT广泛用于授权。声明与时间窗口校验可防止伪造与重放,算法限定提升安全。

统一规范

  • 算法限定:RS256/ES256,拒绝 none 与弱算法。
  • 声明匹配:aud/iss 精确匹配;nbf/exp 引入±300秒偏移;jti 一次性。

核心实现

验签与声明校验

type Claims = { iss: string; aud: string | string[]; exp: number; nbf?: number; jti?: string }

function enc(s: string): Uint8Array { return new TextEncoder().encode(s) }
function base64urlToBuf(s: string): ArrayBuffer { const b = atob(s.replace(/-/g,'+').replace(/_/g,'/')); const u = new Uint8Array(b.length); for (let i=0;i<b.length;i++) u[i] = b.charCodeAt(i); return u.buffer }

async function importKey(spki: ArrayBuffer, type: 'RS256'|'ES256'): Promise<CryptoKey> { const algo = type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', namedCurve:'P-256' }; return crypto.subtle.importKey('spki', spki, algo as any, false, ['verify']) }

function timeNow(): number { return Math.floor(Date.now() / 1000) }
function validAud(aud: string | string[], expected: string): boolean { return Array.isArray(aud) ? aud.includes(expected) : aud === expected }

class JtiStore { used = new Set<string>(); has(j: string) { return this.used.has(j) } add(j: string) { this.used.add(j) } }

async function verifyJwt(jwt: string, key: CryptoKey, type: 'RS256'|'ES256', expected: { iss: string; aud: string; leewaySec: number }, store = new JtiStore()): Promise<boolean> {
  const parts = jwt.split('.')
  if (parts.length !== 3) return false
  const [h, p, s] = parts
  const header = JSON.parse(new TextDecoder().decode(base64urlToBuf(h)))
  if (header.alg !== type) return false
  const sig = base64urlToBuf(s)
  const ok = await crypto.subtle.verify(type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', hash:'SHA-256' }, key, sig, enc(h + '.' + p))
  if (!ok) return false
  const claims: Claims = JSON.parse(new TextDecoder().decode(base64urlToBuf(p)))
  if (claims.iss !== expected.iss) return false
  if (!validAud(claims.aud, expected.aud)) return false
  const now = timeNow()
  if (typeof claims.nbf === 'number' && claims.nbf - expected.leewaySec > now) return false
  if (claims.exp + expected.leewaySec < now) return false
  if (claims.jti && store.has(claims.jti)) return false
  if (claims.jti) store.add(claims.jti)
  return true
}

落地建议

  • 验签算法限定与公钥可信来源;声明与时间窗口严格校验。
  • 一次性 jti 存储阻断重放;跨环境引入适度时钟偏移。

验证清单

  • 验签是否成功且算法符合要求;aud/iss/nbf/exp/jti 是否校验通过。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部