网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载JWTClaimVerificationOptions 是 jose 库中用于配置 JWT Claims Set即 JWT 载荷部分校验规则的接口覆盖身份类声明iss、aud、sub、typ的期望值匹配、时间类声明iat、nbf、exp的容差与时效校验以及必填声明集合的强制存在性检查。本文以 JWTClaimVerificationOptions.md 为骨架结合 validateClaimsSet 实现 与 verify 测试套件逐一讲解每个选项的含义、类型、默认行为及底层判定逻辑读完即可在 jwtVerify、jwtDecrypt、UnsecuredJWT 等场景中精确配置 Claims Set 校验。一、接口定位它出现在哪些 API 中该接口定义于 src/types.d.ts被三个入口共用src/jwt/verify.ts 中的JWTVerifyOptionsextends VerifyOptions, JWTClaimVerificationOptions供 jwtVerify() 在完成 JWS 签名验证后校验载荷src/jwt/decrypt.ts 中的JWTDecryptOptions供 jwtDecrypt() 在完成 JWE 解密后校验明文载荷src/jwt/unsecured.ts 中的UnsecuredJWT.decode()选项。从调用链看三者最终都汇聚到同一个实现函数 validateClaimsSet(protectedHeader, encodedPayload, options)。该函数先解析 JSON 载荷再依次执行 typ 头校验、声明存在性检查、iss/sub/aud 值匹配、nbf/exp/iat 时间戳判定最后返回JWTPayload。因此理解这一个接口就等于理解了 jose 全库的 JWT 载荷校验策略。二、属性总览该接口全部 8 个属性均为可选?未配置时对应校验直接跳过属性类型作用对象核心作用audience?string \| string[]aud声明期望的 Audience 值设置后强制aud必须存在issuer?string \| string[]iss声明期望的 Issuer 值设置后强制iss必须存在subject?stringsub声明期望的 Subject 值设置后强制sub必须存在typ?stringtyp头参数期望的 JWT Type 头值设置后强制typ头必须存在maxTokenAge?string \| numberiat声明允许的最大 Token 年龄设置后强制iat必须存在requiredClaims?string[]任意声明额外强制必须出现的声明名列表clockTolerance?string \| numbernbf、exp、iat时钟偏差容忍秒数currentDate?Date所有 NumericDate 声明参与时间比较的基准时刻默认new Date()三、身份类声明校验issuer、audience、subject、typ这四类选项的共性在于一旦配置对应声明/头的存在与值匹配会同时被强制要求存在性检查详见下一节 requiredClaims 的默认行为。3.1 issuerIssuer签发者期望的iss值可以是单个字符串或字符串数组任一匹配即通过。源码中的判定为src/lib/jwt_claims_set.ts#L174-L179if ( issuer ! undefined !((Array.isArray(issuer) ? issuer : [issuer]) as unknown[]).includes(payload.iss!) ) { unexpectedClaim(payload, iss) }即单个字符串会被包装成数组后用includes做全等匹配不匹配时抛出JWTClaimValidationFailedclaim为issreason为check_failed。3.2 audienceAudience受众期望的aud值同样支持单个字符串或数组。由于aud本身既可以是字符串也可以是字符串数组源码采用了专门的处理函数src/lib/jwt_claims_set.ts#L83-L95const checkAudiencePresence (audPayload: unknown, audOption: unknown[]) { if (typeof audPayload string) { return audOption.includes(audPayload) } if (Array.isArray(audPayload)) { // Each principal intended to process the JWT MUST // identify itself with a value in the audience claim return audOption.some((aud) audPayload.includes(aud)) } return false }要点载荷中aud是字符串时必须与期望值全等载荷中aud是数组时遵循 RFC 7519 语义每个处理 JWT 的主体必须在aud中找到自己的标识因此要求期望数组与载荷数组存在交集someincludes载荷中aud缺失或类型非法既非字符串也非数组时返回false校验失败。3.3 subjectSubject主体期望的sub值仅支持单个字符串src/lib/jwt_claims_set.ts#L181-L183payload.sub ! subject即失败。注意sub的期望值比较是严格全等且不像aud那样有数组形式。3.4 typType类型头参数与前三者不同typ校验的对象是JWT 的 protected header 而非载荷声明src/lib/jwt_claims_set.ts#L140-L152。它要求 protected header 中必须存在字符串类型的typ且规范化后与期望值相等。规范化规则见 normalizeTypconst normalizeTyp (value: string) { const normalized value.toLowerCase() return value.includes(/) ? normalized : application/${normalized} }即期望值不包含/时会被自动补上application/前缀匹配也忽略大小写。例如传入typ: JWT实际期望头值为application/JWT传入typ: application/jwt也能匹配application/JWT。校验失败时抛出JWTClaimValidationFailedclaim为typ。四、时间类声明校验clockTolerance、currentDate、maxTokenAge这三者共同控制 jose 对 NumericDate 声明iat、nbf、exp的时间比较行为。比较基准统一取自currentDate默认new Date()并通过 epoch()Math.floor(date.getTime() / 1000)换算为 Unix 秒。4.1 currentDate可控的时间基准类型为Date默认值为new Date()调用时刻的当前时间。源码在 src/lib/jwt_claims_set.ts#L205-L209 中将其转换为秒并经过validateInput有限性校验。它允许在测试环境或离线场景中伪造当前时刻从而稳定地验证过期、未生效等时间相关逻辑传入非法值如NaN会抛出TypeError(Invalid currentDate option input)。4.2 clockTolerance时钟偏差容忍度用于吸收签发方与验证方之间时钟不同步造成的误判传入number如5时直接视为秒数传入string如5 seconds、10 minutes、2 hours时由 secs() 解析为秒数。其作用于三处比较src/lib/jwt_claims_set.ts#L214-L231nbf生效时间当nbf now tolerance时判定失败exp过期时间当exp now - tolerance时抛出JWTExpirediat签发时间仅在设置maxTokenAge时见 4.3。也就是说clockTolerance为验证方往前和往后各放宽了容忍窗口。测试用例test/jwt/verify.test.ts#L324-L330同时验证了数字形式clockTolerance: 1与字符串形式clockTolerance: 1s的等价性L541-L544 则确认传入NaN、Infinity、-Infinity时会被拒绝TypeError。4.3 maxTokenAgeToken 最大年龄该选项用于限制 Token 从签发iat到被验证时刻之间的最大时间跨度数字形式按秒计如5字符串形式同样经secs()解析如10 minutes设置该选项后iat声明变为必需见下节默认存在性规则校验逻辑见 src/lib/jwt_claims_set.ts#L232-L256const age now - iat! const max validateInput( maxTokenAge option, typeof maxTokenAge number ? maxTokenAge : secs(maxTokenAge), ) if (age - tolerance max) { throw new JWTExpired(iat claim timestamp check failed (too far in the past), ...) } if (age -tolerance) { throw new JWTClaimValidationFailed( iat claim timestamp check failed (it should be in the past), ...) }两个方向的判定age - tolerance maxToken 太老抛出JWTExpired码ERR_JWT_EXPIREDage -toleranceiat落在未来且超出容忍范围说明签发时间非法抛出JWTClaimValidationFailed。注意clockTolerance在这里同样生效它同时放宽年龄上限和未来签发两种判定。典型场景是会话有效期控制签发方在登录时写入iat验证方通过maxTokenAge: 7 days强制会话最多存活 7 天即使exp未设置或设置更长也无效。五、存在性校验requiredClaims 及其默认规则requiredClaims接收一个字符串数组数组中的每个声明名必须出现在 JWT Claims Set 中仅检查存在不检查值缺失时抛出JWTClaimValidationFailedreason为missing。其默认行为是自动叠加由其他选项推导出的必填声明src/lib/jwt_claims_set.ts#L154-L172const { requiredClaims [], issuer, subject, audience, maxTokenAge } options const presenceCheck [...requiredClaims] if (maxTokenAge ! undefined) presenceCheck.push(iat) if (audience ! undefined) presenceCheck.push(aud) if (subject ! undefined) presenceCheck.push(sub) if (issuer ! undefined) presenceCheck.push(iss) for (const claim of new Set(presenceCheck.reverse())) { if (!Object.hasOwn(payload, claim)) { throw new JWTClaimValidationFailed(missing required ${claim} claim, ...) } }规则归纳配置的选项被强制要求存在的声明issuer被设置issaudience被设置audsubject被设置submaxTokenAge被设置iatrequiredClaims中列出所列全部声明实现细节presenceCheck.reverse()后放入Set去重保证无论用户自定义列表顺序如何都不重复检查、也不遗漏。Object.hasOwn仅检查自有属性因此__proto__之类继承属性不会误判为存在。六、时间字符串解析secs() 与支持的格式clockTolerance与maxTokenAge的字符串形式统一由 secs(str) 解析。其正则src/lib/jwt_claims_set.ts#L17-L18支持单位s/secs/second(s)、m/mins/minute(s)、h/hrs/hour(s)、d/day(s)、w/week(s)、y/yrs/year(s)数值整数或小数如1.5 hours允许前置空格方向后缀ago过去取负值与from now未来取正值但不能与正负号同时使用匹配忽略大小写。各单位换算系数见 multiplierss1、m60、h3600、d86400、w604800、y31557600一年按 365.25 天计。格式非法时抛出TypeError(Invalid time period format)。常见写法示例{ clockTolerance: 5, // 秒数字 clockTolerance: 5 seconds, // 秒字符串 clockTolerance: 10 minutes, // 600 秒 clockTolerance: 2 hours, // 7200 秒 maxTokenAge: 7 days, // 604800 秒 }七、配套错误类型校验失败如何处理Claims Set 校验失败时抛出两类错误二者都实现 JWTClaimValidationFailure 结构含claim、reason、payload三个字段类定义在 src/util/errors.tsJWTClaimValidationFailedcode为ERR_JWT_CLAIM_VALIDATION_FAILED覆盖值不匹配check_failed、类型非法invalid、声明缺失missing等所有非过期类失败JWTExpiredcode为ERR_JWT_EXPIRED专门用于exp过期与maxTokenAge超龄两类过期判定。注意JWTExpired并不继承JWTClaimValidationFailedsrc/util/errors.ts 的注释明确说明这一点因此单独用instanceof JWTClaimValidationFailed无法覆盖过期场景。官方推荐的写法是使用联合类型 JWTClaimValidationError 或按err.code判别import { jwtVerify } from jose import { errors } from jose try { await jwtVerify(jwt, key, { issuer: urn:example:issuer, audience: urn:example:audience }) } catch (err) { switch (err.code) { case ERR_JWT_EXPIRED: // 处理过期err 收窄为 JWTExpired break case ERR_JWT_CLAIM_VALIDATION_FAILED: // 处理其他声明校验失败err 收窄为 JWTClaimValidationFailed break default: throw err } }另一个关键事实Claims Set 校验总是发生在签名验证或解密成功之后见 src/jwt/verify.ts#L174-L187 中verifyCompact之后才调用validateClaimsSet因此捕获到上述错误时可以确信载荷确实来自持有私钥/密钥的签发方。八、组合实战一个完整的验证配置结合 jwtVerify() 文档中的对称密钥示例一个同时使用身份与时间校验的完整配置如下import { jwtVerify, createRemoteJWKSet } from jose // 方式一对称密钥 const secret new TextEncoder().encode( cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2, ) const jwt eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2MjMxLCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.C4iSlLfAUMBq--wnC6VqD9gEOhwpRZpoRarE0m7KEnI const { payload, protectedHeader } await jwtVerify(jwt, secret, { issuer: urn:example:issuer, // 强制 iss 存在且匹配 audience: urn:example:audience, // 强制 aud 存在且匹配 subject: urn:example:subject, // 强制 sub 存在且匹配 typ: JWT, // 强制 protected header 的 typ 为 application/JWT maxTokenAge: 5 minutes, // 强制 iat 存在且 Token 年龄不超过 5 分钟 clockTolerance: 30 seconds, // 时间比较放宽 30 秒吸收时钟偏差 requiredClaims: [urn:example:claim], // 额外强制自定义声明存在 }) console.log(protectedHeader) console.log(payload)// 方式二远程 JWKS 动态密钥解析 const JWKS createRemoteJWKSet(new URL(https://www.googleapis.com/oauth2/v3/certs)) const { payload, protectedHeader, key } await jwtVerify(jwt, JWKS, { issuer: urn:example:issuer, audience: urn:example:audience, })方式二演示了jwtVerify的 getKey 重载传入密钥解析函数时返回值中会额外携带key字段即实际用于验证的密钥见 src/jwt/verify.ts#L184-L186。远程 JWKS 的创建与缓存行为见 createRemoteJWKSet。生产环境建议的最小配置组合是issueraudiencemaxTokenAge或依赖exp 适度的clockTolerance。这样既验证了 Token 的身份归属防伪造与串用又通过时间窗口防止重放与长期有效 Token 的滥用。九、测试佐证与边界行为test/jwt/verify.test.ts 是验证该接口行为最直接的证据除上文提及的用例外还包括clockTolerance: 0时不产生任何放宽L565即默认零容忍currentDate可人为拨动系统时间用于测试过期/未生效逻辑非法数值NaN、±Infinity传入clockTolerance、maxTokenAge会抛TypeErrorsrc/lib/jwt_claims_set.ts#L203、L234-L237aud缺失或格式错误时校验失败且validateAudienceClaim会在签发侧JWTClaimsBuilder.setAudience就拒绝非字符串/非字符串数组的输入保证签发出的 JWT 载荷形态合法。此外签发侧的 JWTClaimsBuilder被 SignJWT 与 EncryptJWT 使用与验证侧共享同一套声明形态校验setIssuer/setSubject/setJti要求字符串setAudience要求字符串或字符串数组setNotBefore/setExpirationTime/setIssuedAt支持数字Unix 秒、Date或时间字符串经secs()解析setIssuedAt无参时默认取当前时刻。理解两端的一致性有助于在签发方与验证方之间设计出对称、可预期的校验配置。十、小结选型速查需求选项连带效应校验签发者issuer强制iss存在校验受众audience强制aud存在校验主体subject强制sub存在校验头类型typ强制 protected header 的typ存在限制 Token 年龄maxTokenAge强制iat存在吸收时钟偏差clockTolerance放宽nbf/exp/iat判定窗口固定比较基准时刻currentDate替代new Date()参与比较追加必填声明requiredClaims叠加在上述自动规则之上JWTClaimVerificationOptions 的全部选项都遵循可选、不配置即跳过的设计哲学让开发者按需渐进加固校验策略而所有校验最终都统一收敛于 validateClaimsSet保证了 jwtVerify、jwtDecrypt、UnsecuredJWT 三条路径上行为完全一致。赞分享网络安全认证鉴权后端【免费下载链接】joseJWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes项目地址https://gitcode.com/gh_mirrors/jo/jose点击查看免费下载相关推荐jose 中的 decodeJwt免验签解析 JWT Claims Set 的完整指南jose 中的 decodeJwt免验签解析 JWT Claims Set 的完整指南 decodeJwt 是 jose 库中用于解析 JSON Web To网络安全认证鉴权后端jose 中 jwtVerify 函数完全指南JWT 签名验证与 Claims Set 校验实战jose 中 jwtVerify 函数完全指南JWT 签名验证与 Claims Set 校验实战 jose 项目为 Node.js、浏览器、Cloudflar网络安全认证鉴权后端jose JWT 验证实战jwtVerify 的签名验证与 Claims Set 校验全指南jose JWT 验证实战jwtVerify 的签名验证与 Claims Set 校验全指南 导读 本指南聚焦 jose 库中 JWTJSON Web To网络安全认证鉴权后端上一篇Onebox与Discourse的完美结合论坛内容富媒体化实战指南下一篇yuzu模拟器终极指南在PC上免费畅玩Switch游戏的完整解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考