网络安全认证鉴权后端【免费下载链接】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 库中createRemoteJWKSet返回的密钥解析函数RemoteJWKSet系统讲解其函数签名、实例属性、内置的缓存与冷却cooldown机制以及jwksCache、customFetch、RemoteJWKSetOptions等配套选项的完整用法。读者读完将掌握如何在 Node.js、浏览器、Cloudflare Workers、Deno、Bun 等 Web-interoperable 运行时中基于 OAuth 2.0 / OIDC 的jwks_uri端点实现 JWT 验签的自动密钥发现、缓存刷新与错误处理并能结合源码理解其底层实现。一、RemoteJWKSet 是什么RemoteJWKSet是调用createRemoteJWKSet后返回的一个可调用对象callable它本身是一个异步函数用于把 JWS JOSE Header 解析resolve为用于验签的公钥对象同时对象身上还挂载了若干描述其内部缓存状态的只读属性与方法。完整定义见 src/jwks/remote.ts 与 接口文档。它可以直接传给jwtVerify以及所有接受密钥解析函数的消费方如compactVerify、flattenedVerify、generalVerify等相关说明见 jwtVerify 文档。也就是说你几乎不需要直接调用RemoteJWKSet本身——把它作为getKey参数传给验签 API 即可。函数签名▸ RemoteJWKSet( protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput, ): PromiseCryptoKey参数类型说明protectedHeader?JWSHeaderParametersJWS 受保护头包含alg、kid等用于选键的参数token?FlattenedJWSInput扁平化 JWS 输入含protected、payload、signature等字段返回值PromiseCryptoKey即解析得到的 Web Crypto API 公钥对象。[!NOTE] 该函数的用途是解析验签用公钥不会用于公钥加密场景。二、实例属性与方法详解RemoteJWKSet除了可调用之外还通过Object.defineProperties安装了五个实例成员实现见 src/jwks/remote.ts用于观测和控制内部的 JWKS 缓存状态。成员类型语义coolingDownreadonly boolean自上次成功 fetch 之后冷却窗口是否仍在生效即是否处于冷却期内freshreadonly boolean当前缓存的 JWKS 是否仍在其 cacheMaxAge 有效期之内reloadingreadonly boolean是否正有一个 JWKS fetch 请求在途in flightreload()() Promisevoid主动触发一次 JWKS fetch绕过冷却期jwks()() JSONWebKeySet \| undefined返回当前缓存的JSONWebKeySet尚未 fetch 或未通过jwksCache播种时返回undefined三个布尔状态的生命周期从 test/jwks/remote.test.ts 的createRemoteJWKSet manual reload测试可以清楚看到各状态的流转创建之初尚未发生任何 fetchcoolingDown false、fresh false、reloading false且jwks()返回undefined首次 fetch 成功后coolingDown与fresh立即变为truefresh取决于cacheMaxAgecoolingDown取决于cooldownDuration调用reload()期间reloading truefetch 完成后回到false手动修改jwks()返回值无效测试中JWKS.jwks()!.keys []之后再验签依然成功说明该函数返回的是内部不可变的快照/代理视图直接改返回对象不会污染内部缓存。reload() 的语义reload()是唯一主动刷新手段它绕过cooldownDuration的冷却限制强制拉取一次 JWKS。结合jwksCache使用时成功 fetch 的结果也会同步写回外部缓存对象见下文。三、缓存与节流RemoteJWKSet 的底层行为要正确使用RemoteJWKSet必须理解其背后的两级缓存策略实现见 src/jwks/remote.ts首次解析或缓存过期时若本地没有缓存或缓存已超过cacheMaxAge先触发一次 fetch本地解析失败时若JWKSNoMatchingKey无匹配密钥抛出且距离上次成功 fetch 已超过cooldownDuration则再次 fetch 并重试一次解析——这是为了让远端轮换密钥后本地缓存能在冷却结束后尽快自动更新冷却期内不会因为“无匹配密钥”而反复打爆远端端点这是防止滥用abuse的核心设计。整个 fetch 过程由内部fetchJwks完成src/jwks/remote.ts它要求 HTTP 响应必须是200并将响应体解析为 JSON超时或解析失败分别抛出JWKSTimeout与JOSEError。并发与序列保护源码通过reloadSequence/appliedSequence两个递增计数器确保较旧的 fetch 结果不会覆盖较新的结果见 src/jwks/remote.ts 与 test/jwks/remote.test.ts 的 workerd 并发测试同时在 Cloudflare Workers 等隔离型运行时中若存在上一个请求遗留的 in-flight fetch会被主动作废pendingFetch undefined见 src/jwks/remote.ts避免旧请求的 Promise 干扰新请求。选键规则选键严格遵循 RFC 语义先用 Header 的alg决定 JWK 的kty再用kid匹配 JWK 的kid若 Header 中存在同时尊重 JWK 上的use如sig与key_ops。必须恰好匹配到一个公钥匹配不到抛JWKSNoMatchingKey匹配到多个抛JWKSMultipleMatchingKeys该错误可迭代见 src/util/errors.ts。四、快速上手创建并使用 RemoteJWKSetconst JWKS jose.createRemoteJWKSet(new URL(https://www.googleapis.com/oauth2/v3/certs)) const { payload, protectedHeader } await jose.jwtVerify(jwt, JWKS, { issuer: urn:example:issuer, audience: urn:example:audience, }) console.log(protectedHeader) console.log(payload)这是 官方示例 中的标准用法createRemoteJWKSet只接收URL与可选的RemoteJWKSetOptions返回的JWKS函数直接充当jwtVerify的密钥解析器。jwtVerify内部会依次调用RemoteJWKSet(protectedHeader, token)来解析公钥见 src/jwt/verify.ts 的消费方式。导入方式该能力从主入口jose以及子路径jose/jwks/remote均有命名导出相关导出见 src/index.tsimport { createRemoteJWKSet, jwksCache, customFetch } from jose // 或 import { createRemoteJWKSet, jwksCache, customFetch } from jose/jwks/remote五、RemoteJWKSetOptions全部可配置项创建RemoteJWKSet时可传入以下选项完整说明见 RemoteJWKSetOptions 文档默认值与校验逻辑见 src/jwks/remote.ts 与validateDurationsrc/jwks/remote.ts选项类型默认值说明timeoutDurationnumber50005 秒HTTP 请求超时时间毫秒。超时后请求被中止、验签失败。必须是非负整数cooldownDurationnumber3000030 秒上次成功 fetch 后的一段时间毫秒内不再触发新的 HTTP 请求防止滥用。不能为NaNcacheMaxAgenumber60000010 分钟两次成功 HTTP 请求之间的最大间隔毫秒即缓存有效期。不能为NaN源码类型上还允许InfinityheadersRecordstring, string—随 HTTP 请求发送的额外请求头[jwksCache]JWKSCacheInput—外部可写缓存对象见下文[customFetch]FetchImplementation全局fetch自定义 fetch 实现关于默认请求头创建解析器时源码会为请求设置默认请求头src/jwks/remote.tsUser-Agentjose/v6.2.10浏览器环境为避免触发不必要的 CORS preflight 而省略acceptapplication/json, application/jwk-setjson若你未自定义。test/jwks/remote.test.ts中对user-agent头做了断言test/jwks/remote.test.ts说明该默认值是可观测的。六、jwksCache面向无状态云运行时的持久缓存jwksCache是一个unique symbolsrc/jwks/remote.ts专为无法在两次调用之间保留内存缓存的云函数/边缘运行时设计如某些 Serverless 与 Workers 环境。[!WARNING] 该选项存在安全影响必须保证 JWKS 缓存对象只能被你自己的代码写入否则可能被注入恶意公钥。传入jwksCache后你提供的可写对象承担两个职责见 jwksCache 文档作为初始缓存如果对象携带合法的jwks与uat且uat仍在cacheMaxAge内解析器会直接用它构建本地选键器免去首次 HTTP 请求作为回写目标成功 fetch 后解析器会把新的jwks与uat写回该对象。缓存对象的结构为ExportedJWKSCache{ jwks: JSONWebKeySet; uat: number }其中uat是“最后更新时间”毫秒时间戳。输入类型JWKSCacheInput允许ExportedJWKSCache或空对象{}类型别名。推荐使用模式// 前提从低延迟 KV 存储拉取上次缓存 let getPreviouslyCachedJWKS!: () Promisejose.ExportedJWKSCache let storeNewJWKScache!: (cache: jose.ExportedJWKSCache) Promisevoid const jwksCache: jose.JWKSCacheInput (await getPreviouslyCachedJWKS()) || {} const { uat } jwksCache const JWKS jose.createRemoteJWKSet(url, { [jose.jwksCache]: jwksCache, }) await jose.jwtVerify(jwt, JWKS) if (uat ! jwksCache.uat) { await storeNewJWKScache(jwksCache) }核心逻辑验签前先取缓存无则{}验签后对比uat是否变化变化才回写存储。这避免了每次冷启动都重新拉取 JWKS也避免了在每次调用中把整份 JWKS 打进存储。源码中的回写发生在reload内部src/jwks/remote.ts。七、customFetch自定义 fetch 实现customFetch同样是unique symbolsrc/jwks/remote.ts用于把解析器内部的 HTTP 请求替换成你自己的实现从而获得代理、重试、日志、测试 mock 等能力。其类型FetchImplementation的签名为type FetchImplementation ( url: string, options: { headers: Headers method: GET redirect: manual signal: AbortSignal }, ) PromiseResponse[!NOTE] 已知坑把options透传给 ky 等 fetch 类库时大概率遇到类型不匹配这些库的 typings 几乎不与原生 fetch 完全对齐建议使用ts-expect-error处理。用 ky 实现重试与日志import ky from ky const JWKS jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) ky(args[0], { ...args[1], hooks: { beforeRequest: [(request) { logRequest(request) }], beforeRetry: [({ request, error, retryCount }) { logRetry(request, error, retryCount) }], afterResponse: [(request, _, response) { logResponse(request, response) }], }, }), })用 undici 接入 HTTP 代理import * as undici from undici let envHttpProxyAgent new undici.EnvHttpProxyAgent() const JWKS jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) // ts-ignore undici.fetch(args[0], { ...args[1], dispatcher: envHttpProxyAgent }), })用 undici 自动重试网络错误import * as undici from undici let retryAgent new undici.RetryAgent(new undici.Agent(), { statusCodes: [], errorCodes: [ ECONNRESET, ECONNREFUSED, ENOTFOUND, ENETDOWN, ENETUNREACH, EHOSTDOWN, UND_ERR_SOCKET, ], }) const JWKS jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) // ts-ignore undici.fetch(args[0], { ...args[1], dispatcher: retryAgent }), })用 undici MockAgent 在测试中模拟响应import * as undici from undici let mockAgent new undici.MockAgent() mockAgent.disableNetConnect() const JWKS jose.createRemoteJWKSet(url, { [jose.customFetch]: (...args) // ts-ignore undici.fetch(args[0], { ...args[1], dispatcher: mockAgent }), })这一模式正是 test/jwks/remote.test.ts 所采用的测试策略整个测试套件通过 MockAgent 拦截https://as.example.com/jwks的响应并用timekeeper冻结/拨快时间轴来验证冷却与过期行为。八、错误处理与多密钥迭代远程 JWKS 场景最常见的错误集中在 src/util/errors.ts 中定义均可通过jose.errors访问错误类code触发条件JWKSNoMatchingKeyERR_JWKS_NO_MATCHING_KEYJWKS 中没有任何可用密钥匹配选键条件JWKSMultipleMatchingKeysERR_JWKS_MULTIPLE_MATCHING_KEYS有多个密钥同时匹配可迭代以逐个尝试验签JWKSTimeoutERR_JWKS_TIMEOUTfetch 超时对应timeoutDuration多密钥匹配时的迭代验签默认策略是“恰好一个匹配”若远端 JWKS 中存在多个满足条件的公钥例如两把 RSA 密钥都未声明algjwtVerify会抛JWKSMultipleMatchingKeys。此时可以显式迭代该错误逐一尝试验签const options { issuer: urn:example:issuer, audience: urn:example:audience, } const { payload, protectedHeader } await jose .jwtVerify(jwt, JWKS, options) .catch(async (error) { if (error instanceof jose.errors.JWKSMultipleMatchingKeys) { for await (const publicKey of error) { try { return await jose.jwtVerify(jwt, publicKey, options) } catch (innerError) { if (innerError instanceof jose.errors.JWSSignatureVerificationFailed) { continue } throw innerError } } throw new jose.errors.JWSSignatureVerificationFailed() } throw error }) console.log(protectedHeader) console.log(payload)这里error本身是异步可迭代的async iterable每次产出的是一个public类型的CryptoKey只有JWSSignatureVerificationFailed会被吞掉继续尝试下一个密钥其余错误直接向上抛出。测试 test/jwks/remote.test.ts 验证了这一迭代行为并确认两次迭代产生的密钥对象是同一个内部有 WeakSet 缓存。九、实现要点小结RemoteJWKSet 可调用函数 5 个实例成员coolingDown、fresh、reloading、reload、jwks全部由createRemoteJWKSet在 src/jwks/remote.ts 中安装两级节流cacheMaxAge10 分钟默认控制缓存新鲜度cooldownDuration30 秒默认控制失败重试时的 fetch 频率timeoutDuration5 秒默认控制单次请求上限无状态运行时用jwksCache做外部持久缓存验签前后比对uat决定是否回写高级网络需求代理、重试、日志、mock通过customFetch以 symbol 选项注入实测行为与上述语义完全对应见 test/jwks/remote.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 本地 JWKS 公钥解析createLocalJWKSet 完全指南jose 本地 JWKS 公钥解析createLocalJWKSet 完全指南 本篇技术指南围绕 jose 库的 createLocalJWKSet 展开讲网络安全认证鉴权后端jose 通用 JWS 验证动态密钥解析GeneralVerifyGetKey 接口全解析jose 通用 JWS 验证动态密钥解析GeneralVerifyGetKey 接口全解析 通用 JSON 序列化General JSON Serializ网络安全认证鉴权后端scalar/highlight 源码级解析Scalar 零依赖高性能语法高亮引擎与 40 种语言语法体系scalar/highlight 源码级解析Scalar 零依赖高性能语法高亮引擎与 40 种语言语法体系 本篇技术指南围绕 Scalar 开源仓库中的代码网络安全认证鉴权后端上一篇5分钟掌握AMD锐龙SMU调试工具释放处理器隐藏性能的完整方案下一篇如何掌握AMD锐龙SDT调试工具从入门到精通的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
