
網(wǎng)絡安全認證鑒權后端【免費下載鏈接】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返回的密鑰解析函數(shù)RemoteJWKSet系統(tǒng)講解其函數(shù)簽名、實例屬性、內(nèi)置的緩存與冷卻cooldown機制以及jwksCache、customFetch、RemoteJWKSetOptions等配套選項的完整用法。讀者讀完將掌握如何在 Node.js、瀏覽器、Cloudflare Workers、Deno、Bun 等 Web-interoperable 運行時中基于 OAuth 2.0 / OIDC 的jwks_uri端點實現(xiàn) JWT 驗簽的自動密鑰發(fā)現(xiàn)、緩存刷新與錯誤處理并能結(jié)合源碼理解其底層實現(xiàn)。一、RemoteJWKSet 是什么RemoteJWKSet是調(diào)用createRemoteJWKSet后返回的一個可調(diào)用對象callable它本身是一個異步函數(shù)用于把 JWS JOSE Header 解析resolve為用于驗簽的公鑰對象同時對象身上還掛載了若干描述其內(nèi)部緩存狀態(tài)的只讀屬性與方法。完整定義見 src/jwks/remote.ts 與 接口文檔。它可以直接傳給jwtVerify以及所有接受密鑰解析函數(shù)的消費方如compactVerify、flattenedVerify、generalVerify等相關說明見 jwtVerify 文檔。也就是說你幾乎不需要直接調(diào)用RemoteJWKSet本身——把它作為getKey參數(shù)傳給驗簽 API 即可。函數(shù)簽名? RemoteJWKSet( protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput, ): PromiseCryptoKey參數(shù)類型說明protectedHeader?JWSHeaderParametersJWS 受保護頭包含alg、kid等用于選鍵的參數(shù)token?FlattenedJWSInput扁平化 JWS 輸入含protected、payload、signature等字段返回值PromiseCryptoKey即解析得到的 Web Crypto API 公鑰對象。[!NOTE] 該函數(shù)的用途是解析驗簽用公鑰不會用于公鑰加密場景。二、實例屬性與方法詳解RemoteJWKSet除了可調(diào)用之外還通過Object.defineProperties安裝了五個實例成員實現(xiàn)見 src/jwks/remote.ts用于觀測和控制內(nèi)部的 JWKS 緩存狀態(tài)。成員類型語義coolingDownreadonly boolean自上次成功 fetch 之后冷卻窗口是否仍在生效即是否處于冷卻期內(nèi)freshreadonly boolean當前緩存的 JWKS 是否仍在其 cacheMaxAge 有效期之內(nèi)reloadingreadonly boolean是否正有一個 JWKS fetch 請求在途in flightreload()() Promisevoid主動觸發(fā)一次 JWKS fetch繞過冷卻期jwks()() JSONWebKeySet \| undefined返回當前緩存的JSONWebKeySet尚未 fetch 或未通過jwksCache播種時返回undefined三個布爾狀態(tài)的生命周期從 test/jwks/remote.test.ts 的createRemoteJWKSet manual reload測試可以清楚看到各狀態(tài)的流轉(zhuǎn)創(chuàng)建之初尚未發(fā)生任何 fetchcoolingDown false、fresh false、reloading false且jwks()返回undefined首次 fetch 成功后coolingDown與fresh立即變?yōu)閠ruefresh取決于cacheMaxAgecoolingDown取決于cooldownDuration調(diào)用reload()期間reloading truefetch 完成后回到false手動修改jwks()返回值無效測試中JWKS.jwks()!.keys []之后再驗簽依然成功說明該函數(shù)返回的是內(nèi)部不可變的快照/代理視圖直接改返回對象不會污染內(nèi)部緩存。reload() 的語義reload()是唯一主動刷新手段它繞過cooldownDuration的冷卻限制強制拉取一次 JWKS。結(jié)合jwksCache使用時成功 fetch 的結(jié)果也會同步寫回外部緩存對象見下文。三、緩存與節(jié)流RemoteJWKSet 的底層行為要正確使用RemoteJWKSet必須理解其背后的兩級緩存策略實現(xiàn)見 src/jwks/remote.ts首次解析或緩存過期時若本地沒有緩存或緩存已超過cacheMaxAge先觸發(fā)一次 fetch本地解析失敗時若JWKSNoMatchingKey無匹配密鑰拋出且距離上次成功 fetch 已超過cooldownDuration則再次 fetch 并重試一次解析——這是為了讓遠端輪換密鑰后本地緩存能在冷卻結(jié)束后盡快自動更新冷卻期內(nèi)不會因為“無匹配密鑰”而反復打爆遠端端點這是防止濫用abuse的核心設計。整個 fetch 過程由內(nèi)部fetchJwks完成src/jwks/remote.ts它要求 HTTP 響應必須是200并將響應體解析為 JSON超時或解析失敗分別拋出JWKSTimeout與JOSEError。并發(fā)與序列保護源碼通過reloadSequence/appliedSequence兩個遞增計數(shù)器確保較舊的 fetch 結(jié)果不會覆蓋較新的結(jié)果見 src/jwks/remote.ts 與 test/jwks/remote.test.ts 的 workerd 并發(fā)測試同時在 Cloudflare Workers 等隔離型運行時中若存在上一個請求遺留的 in-flight fetch會被主動作廢pendingFetch undefined見 src/jwks/remote.ts避免舊請求的 Promise 干擾新請求。選鍵規(guī)則選鍵嚴格遵循 RFC 語義先用 Header 的alg決定 JWK 的kty再用kid匹配 JWK 的kid若 Header 中存在同時尊重 JWK 上的use如sig與key_ops。必須恰好匹配到一個公鑰匹配不到拋JWKSNoMatchingKey匹配到多個拋JWKSMultipleMatchingKeys該錯誤可迭代見 src/util/errors.ts。四、快速上手創(chuàng)建并使用 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函數(shù)直接充當jwtVerify的密鑰解析器。jwtVerify內(nèi)部會依次調(diào)用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全部可配置項創(chuàng)建RemoteJWKSet時可傳入以下選項完整說明見 RemoteJWKSetOptions 文檔默認值與校驗邏輯見 src/jwks/remote.ts 與validateDurationsrc/jwks/remote.ts選項類型默認值說明timeoutDurationnumber50005 秒HTTP 請求超時時間毫秒。超時后請求被中止、驗簽失敗。必須是非負整數(shù)cooldownDurationnumber3000030 秒上次成功 fetch 后的一段時間毫秒內(nèi)不再觸發(fā)新的 HTTP 請求防止濫用。不能為NaNcacheMaxAgenumber60000010 分鐘兩次成功 HTTP 請求之間的最大間隔毫秒即緩存有效期。不能為NaN源碼類型上還允許InfinityheadersRecordstring, string—隨 HTTP 請求發(fā)送的額外請求頭[jwksCache]JWKSCacheInput—外部可寫緩存對象見下文[customFetch]FetchImplementation全局fetch自定義 fetch 實現(xiàn)關于默認請求頭創(chuàng)建解析器時源碼會為請求設置默認請求頭src/jwks/remote.tsUser-Agentjose/v6.2.10瀏覽器環(huán)境為避免觸發(fā)不必要的 CORS preflight 而省略acceptapplication/json, application/jwk-setjson若你未自定義。test/jwks/remote.test.ts中對user-agent頭做了斷言test/jwks/remote.test.ts說明該默認值是可觀測的。六、jwksCache面向無狀態(tài)云運行時的持久緩存jwksCache是一個unique symbolsrc/jwks/remote.ts專為無法在兩次調(diào)用之間保留內(nèi)存緩存的云函數(shù)/邊緣運行時設計如某些 Serverless 與 Workers 環(huán)境。[!WARNING] 該選項存在安全影響必須保證 JWKS 緩存對象只能被你自己的代碼寫入否則可能被注入惡意公鑰。傳入jwksCache后你提供的可寫對象承擔兩個職責見 jwksCache 文檔作為初始緩存如果對象攜帶合法的jwks與uat且uat仍在cacheMaxAge內(nèi)解析器會直接用它構(gòu)建本地選鍵器免去首次 HTTP 請求作為回寫目標成功 fetch 后解析器會把新的jwks與uat寫回該對象。緩存對象的結(jié)構(gòu)為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也避免了在每次調(diào)用中把整份 JWKS 打進存儲。源碼中的回寫發(fā)生在reload內(nèi)部src/jwks/remote.ts。七、customFetch自定義 fetch 實現(xiàn)customFetch同樣是unique symbolsrc/jwks/remote.ts用于把解析器內(nèi)部的 HTTP 請求替換成你自己的實現(xiàn)從而獲得代理、重試、日志、測試 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 實現(xiàn)重試與日志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 自動重試網(wǎng)絡錯誤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凍結(jié)/撥快時間軸來驗證冷卻與過期行為。八、錯誤處理與多密鑰迭代遠程 JWKS 場景最常見的錯誤集中在 src/util/errors.ts 中定義均可通過jose.errors訪問錯誤類code觸發(fā)條件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每次產(chǎn)出的是一個public類型的CryptoKey只有JWSSignatureVerificationFailed會被吞掉繼續(xù)嘗試下一個密鑰其余錯誤直接向上拋出。測試 test/jwks/remote.test.ts 驗證了這一迭代行為并確認兩次迭代產(chǎn)生的密鑰對象是同一個內(nèi)部有 WeakSet 緩存。九、實現(xiàn)要點小結(jié)RemoteJWKSet 可調(diào)用函數(shù) 5 個實例成員coolingDown、fresh、reloading、reload、jwks全部由createRemoteJWKSet在 src/jwks/remote.ts 中安裝兩級節(jié)流cacheMaxAge10 分鐘默認控制緩存新鮮度cooldownDuration30 秒默認控制失敗重試時的 fetch 頻率timeoutDuration5 秒默認控制單次請求上限無狀態(tài)運行時用jwksCache做外部持久緩存驗簽前后比對uat決定是否回寫高級網(wǎng)絡需求代理、重試、日志、mock通過customFetch以 symbol 選項注入實測行為與上述語義完全對應見 test/jwks/remote.test.ts。贊分享網(wǎng)絡安全認證鑒權后端【免費下載鏈接】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 展開講網(wǎng)絡安全認證鑒權后端jose 通用 JWS 驗證動態(tài)密鑰解析GeneralVerifyGetKey 接口全解析jose 通用 JWS 驗證動態(tài)密鑰解析GeneralVerifyGetKey 接口全解析 通用 JSON 序列化General JSON Serializ網(wǎng)絡安全認證鑒權后端scalar/highlight 源碼級解析Scalar 零依賴高性能語法高亮引擎與 40 種語言語法體系scalar/highlight 源碼級解析Scalar 零依賴高性能語法高亮引擎與 40 種語言語法體系 本篇技術指南圍繞 Scalar 開源倉庫中的代碼網(wǎng)絡安全認證鑒權后端上一篇5分鐘掌握AMD銳龍SMU調(diào)試工具釋放處理器隱藏性能的完整方案下一篇如何掌握AMD銳龍SDT調(diào)試工具從入門到精通的完整指南創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考