ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

jose JWT 验证结果解析:深入理解 JWTVerifyResult 接口与 jwtVerify 返回值

jose JWT 验证结果解析:深入理解 JWTVerifyResult 接口与 jwtVerify 返回值 网络安全认证鉴权后端【免费下载链接】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点击查看免费下载JWTVerifyResult是 jose 库中jwtVerify()验证签名 JWT 后返回的结果类型封装了 JWT 验证成功后的两大核心产物——JWT Claims Set载荷与 JWS Protected Header受保护头部。本文基于 jose 仓库的接口定义docs/types/interfaces/JWTVerifyResult.md与 src/jwt/verify.ts 的源码实现讲解该接口的类型结构、字段语义、泛型用法、动态密钥解析时的扩展行为以及与之配套的 Claims 校验选项与错误处理帮助你准确消费jwtVerify()的返回值并在 TypeScript 中获得最佳类型推断。一、接口概览一次验证两个产物在 jose 中jwtVerify()用于验证 JWT 格式必须是 JWS Compact 格式、验证 JWS 签名、校验 JWT Claims Set。验证成功后返回PromiseJWTVerifyResultPayloadType其完整定义位于 src/types.d.tsexport interface JWTVerifyResultPayloadType JWTPayload { /** JWT Claims Set. */ payload: PayloadType JWTPayload ([PayloadType] extends [object] ? unknown : unknown extends PayloadType ? unknown : never) /** JWS Protected Header. */ protectedHeader: JWTHeaderParameters }该接口只有两个属性属性类型含义payloadPayloadType JWTPayloadJWT Claims SetJWT 载荷protectedHeaderJWTHeaderParametersJWS Protected Header受保护头部对照 jose 中其他验证结果的同类设计CompactVerifyResult返回的是payload: Uint8Array与protectedHeader: CompactJWSHeaderParameters见 src/types.d.ts。JWTVerifyResult之所以不同是因为 JWT 的载荷是经过 Base64URL 解码并 JSON 解析后的 Claims Set 对象而非原始字节同时其头部必须是包含alg的 Compact 形式。二、payload 属性泛型化的 JWT Claims Setpayload是验证完成后解析出的 JWT 载荷对象。它的类型是PayloadType JWTPayload即调用方声明的载荷类型与jose 内置的 JWTPayload 识别成员的交集。2.1 内置 JWTPayload 识别成员JWTPayload接口docs/types/interfaces/JWTPayload.md定义见 src/types.d.ts声明了 RFC 7519 中七个标准注册 Claims属性类型RFC 定义iss?stringJWT Issuer签发者sub?stringJWT Subject主体aud?string \| string[]JWT Audience受众exp?numberJWT Expiration Time过期时间NumericDatenbf?numberJWT Not Before生效时间NumericDateiat?numberJWT Issued At签发时间NumericDatejti?stringJWT ID令牌唯一标识此外JWTPayload带有索引签名[propName: string]: unknown意味着令牌中携带的任何其他自定义 Claims 也都会被保留在payload对象上。2.2 通过 PayloadType 获得自定义 Claims 的类型推断JWTVerifyResult是泛型接口PayloadType默认值为JWTPayload。在验证你自己的令牌时可以声明预期的 Claims 结构让payload获得精确的类型提示interface MyClaims { role: admin | user org: string } const { payload } await jose.jwtVerifyMyClaims(jwt, secret) // payload.role 已被推断为 admin | user // payload.org 已被推断为 string类型参数的完整说明PayloadType表示该令牌预期携带的 JWT Claims Set 类型定义默认类型为JWTPayload。jose 通过交叉类型PayloadType JWTPayload保证无论自定义声明是否覆盖标准 Claims 的类型始终可用。三、protectedHeader 属性JWS Protected HeaderprotectedHeader是 JWT 的 JWS Protected Header即 Compact JWS 序列化中第一段 Base64URL 解码后的对象类型为JWTHeaderParameters。JWTHeaderParametersdocs/types/interfaces/JWTHeaderParameters.md定义见 src/types.d.ts继承自CompactJWSHeaderParameters后者要求alg字段必须存在src/types.d.ts并在此基础上增加了 JWS 扩展参数b64RFC 7797 的未编码载荷选项。其可识别成员包括属性类型含义algstring必选JWS algAlgorithm头部参数如HS256、RS256b64?booleanRFC 7797 定义的 JWS 载荷表示与签名输入计算扩展crit?string[]JWS critCritical头部参数kid?stringkidKey ID头部参数jku?stringjkuJWK Set URL头部参数jwk?OmitJWK, ...jwkJSON Web Key头部参数仅允许公钥成员typ?stringtypType头部参数cty?stringctyContent Type头部参数x5u?/x5c?/x5t?string/string[]/stringX.509 相关头部参数与JWTPayload一样JWTHeaderParameters也带索引签名令牌中任何其他头部成员都会保留。实践中常通过protectedHeader.alg得知签名算法、通过protectedHeader.kid在 JWKS 中定位用于验签的密钥。四、源码视角jwtVerify 如何构建 JWTVerifyResultjwtVerify()的实现位于 src/jwt/verify.ts返回值的组装逻辑非常清晰export async function jwtVerify(jwt, key, options?) { const verified await verifyCompact(jwt, prepareVerify(options), key) if (!verified[2]) { throw new JWTInvalid(JWTs MUST NOT use unencoded payload) } const payload validateClaimsSet(verified[1], verified[0], options) const result { payload, protectedHeader: verified[1] as types.JWTHeaderParameters } if (typeof key function) { return { ...result, key: verified[3] } } return result }整个调用链可以拆解为三步JWS 签名验证verifyCompact()位于 src/lib/jws_verify.ts完成 JWT 格式检查与 JWS 签名验证返回[payload, protectedHeader, isUnencodedPayload, key]元组拒绝未编码载荷若令牌使用了 RFC 7797 的未编码载荷b64: falsejwtVerify会直接抛出JWTInvalid因为 JWT 不允许未编码的 PayloadClaims Set 校验与解析validateClaimsSet()位于 src/lib/jwt_claims_set.ts将载荷字节严格解码为 UTF-8 并 JSON 解析校验其必须是顶层 JSON 对象否则抛JWTInvalid随后执行iss/sub/aud/nbf/exp/iat/typ/requiredClaims等全部 Claims 校验最后将对象作为payload返回。需要特别注意的是JWTVerifyResult的payload是校验通过后的 Claims Set而非原始字节——这意味着验证失败时函数不会返回该结果而是抛出对应错误详见下文第七节。五、动态密钥解析返回值中的第三个可选字段 keyjwtVerify()提供了三种重载docs/jwt/verify/functions/jwtVerify.md直接传入密钥key: KeyInput返回JWTVerifyResultPayloadType仅payload与protectedHeader传入密钥解析函数getKey: JWTVerifyGetKeyKeyType返回JWTVerifyResultPayloadType ResolvedKeyKeyType即额外携带key字段传入可能是密钥也可能是解析函数的值用于转发场景返回JWTVerifyResultPayloadType PartialResolvedKey——此时key仅在传入了解析函数时才会出现在结果上。ResolvedKey接口docs/types/interfaces/ResolvedKey.md定义见 src/types.d.ts只有一个字段export interface ResolvedKeyKeyType extends CryptoKey | Uint8Array CryptoKey | Uint8Array { /** Key resolved from the key resolver function. */ key: KeyType }动态密钥解析函数JWTVerifyGetKeydocs/jwt/verify/interfaces/JWTVerifyGetKey.md签名如下(protectedHeader: CompactJWSHeaderParameters, token: FlattenedJWSInput) JWK | KeyObject | KeyType | PromiseJWK | KeyObject | KeyType调用时需要注意该函数被调用时令牌的任何组件都尚未被验证因此不能信任函数接收到的protectedHeader与token内容若无法为令牌匹配到合适的密钥应抛出错误而非返回不匹配的密钥。通过收窄KeyType例如解析函数被声明为只返回CryptoKey可以让返回值中的key字段在调用点被精确推断。createRemoteJWKSetdocs/jwks/remote/functions/createRemoteJWKSet.md、createLocalJWKSetdocs/jwks/local/functions/createLocalJWKSet.md与EmbeddedJWKdocs/jwk/embedded/functions/EmbeddedJWK.md都是这种解析函数的现成实现。六、实战三种典型调用方式与结果消费jwtVerify()的完整使用示例见 docs/jwt/verify/functions/jwtVerify.md以下三种场景涵盖了结果对象{ payload, protectedHeader }的典型消费方式。6.1 对称密钥HS256const secret new TextEncoder().encode( cc7e0d44fd473002f1c42167459001140ec6389b7353f8088f4d9a95f2f596f2, ) const jwt eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6dHJ1ZSwiaWF0IjoxNjY5MDU2MjMxLCJpc3MiOiJ1cm46ZXhhbXBsZTppc3N1ZXIiLCJhdWQiOiJ1cm46ZXhhbXBsZTphdWRpZW5jZSJ9.C4iSlLfAUMBq--wnC6VqD9gEOhwpRZpoRarE0m7KEnI const { payload, protectedHeader } await jose.jwtVerify(jwt, secret, { issuer: urn:example:issuer, audience: urn:example:audience, }) console.log(protectedHeader) // { alg: HS256 } console.log(payload) // 校验通过后的 Claims Set6.2 公钥验签RS256SPKI 与 JWK 两种密钥载体const alg RS256 const spki -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwhYOFK2Ocbbpb/zVypi9 ... -----END PUBLIC KEY----- const publicKey await jose.importSPKI(spki, alg) // 也可以使用等价的 JWK 载体 // const publicKey await jose.importJWK({ kty: RSA, n: ..., e: AQAB }, alg) const { payload, protectedHeader } await jose.jwtVerify(jwt, publicKey, { issuer: urn:example:issuer, audience: urn:example:audience, })其中importSPKI与importJWK的用法可参考 docs/key/import/functions/importSPKI.md 与 docs/key/import/functions/importJWK.md。6.3 远程 JWKS 动态解析含 key 字段const JWKS jose.createRemoteJWKSet(new URL(https://www.googleapis.com/oauth2/v3/certs)) const { payload, protectedHeader, key } await jose.jwtVerify(jwt, JWKS, { issuer: urn:example:issuer, audience: urn:example:audience, }) console.log(protectedHeader) console.log(payload) console.log(key) // 本次解析出的实际验签密钥仅使用解析函数时存在七、配套理解校验选项与错误路径要正确理解JWTVerifyResult还需知道哪些校验失败会导致不产生该结果。jwtVerify()的options类型为JWTVerifyOptionsdocs/jwt/verify/interfaces/JWTVerifyOptions.md它是 JWS 验证选项与 JWT Claims 校验选项的组合选项类型行为algorithms?string[]允许的alg值白名单默认允许该密钥可用的全部算法。注意alg: none的未受保护 JWT 永远不会被此 API 接受issuer?string \| string[]期望的issClaim 值设置后issClaim 变为必须存在audience?string \| string[]期望的audClaim 值设置后audClaim 变为必须存在subject?string期望的subClaim 值设置后subClaim 变为必须存在maxTokenAge?string \| number从iat起算的最大存活时间数字表示秒字符串如5 seconds、10 minutes、2 hours设置后iat变为必须存在requiredClaims?string[]必须存在于 Claims Set 中的 Claim 名称数组clockTolerance?string \| number时钟偏移容忍秒数用于nbf/exp以及maxTokenAge下的iat校验currentDate?Date比较 NumericDate Claims 时使用的基准时间默认new Date()crit?{ [name: string]: boolean }声明的 crit 头部参数映射true表示必须受完整性保护typ?string期望的typ头部参数值设置后typ变为必须存在这些校验在validateClaimsSet()中逐项执行失败时抛出的错误类型定义见 docs/util/errors/README.md包括JWSInvalid/JWSSignatureVerificationFailed签名格式或验签失败JWTInvalidClaims Set 不是顶层 JSON 对象、或 JWT 使用了未编码载荷JWTClaimValidationFailediss/sub/aud值不匹配、必需 Claim 缺失、nbf校验失败等JWTExpiredexp已过期或maxTokenAge下iat距今过久docs/util/errors/classes/JWTExpired.md。因此JWTVerifyResult的payload与protectedHeader可以放心地直接用于业务逻辑——它们只会在格式、签名与全部配置的 Claims 校验全部通过后出现。八、小结JWTVerifyResult是 jose 中 JWT 验证链路的最末端产物payload与protectedHeader分别代表了校验通过的 Claims Set与签名头部泛型PayloadType让自定义 Claims 获得精确类型而ResolvedKey的交叉类型则让动态密钥解析场景能够同时获知实际验签密钥。理解它的结构与生成路径是正确使用 docs/jwt/verify/functions/jwtVerify.md 以及测试用例test/jwt/verify.test.ts中各类断言的前提。赞分享网络安全认证鉴权后端【免费下载链接】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 中 CompactVerifyResult 接口完全指南理解紧凑 JWS 校验的返回值结构jose 中 CompactVerifyResult 接口完全指南理解紧凑 JWS 校验的返回值结构 CompactVerifyResult 是 jose 库网络安全认证鉴权后端jose JWT 验证实战jwtVerify 的签名验证与 Claims Set 校验全指南jose JWT 验证实战jwtVerify 的签名验证与 Claims Set 校验全指南 导读 本指南聚焦 jose 库中 JWTJSON Web To网络安全认证鉴权后端jose 中 jwtVerify 函数完全指南JWT 签名验证与 Claims Set 校验实战jose 中 jwtVerify 函数完全指南JWT 签名验证与 Claims Set 校验实战 jose 项目为 Node.js、浏览器、Cloudflar网络安全认证鉴权后端上一篇DeeplxFile文件翻译的新选择大文件也能轻松应对下一篇开源AIGC周刊终极指南每周精选助你掌握AI最新动态创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表