--- title: OAuth PAR与JAR请求对象签名(请求URI/签名验证)最佳实践 keywords: - PAR - JAR - 请求对象 - RS256 - 请求URI description: 通过Pushed Authorization Requests与JWT-secured Authorization Request将授权参数推送并签名,保障授权请求的完整性与可信性。 categories: - 文章资讯 - 技术教程 --- 背景与价值 PAR与JAR可防止授权请求被篡改。推送后以请求URI引用并对请求对象验签,提升安全与可审计性。 统一规范 - 推送参数:使用PAR将参数推送到授权服务器,返回 `request_uri`。 - JAR签名:对请求对象进行 `RS256/ES256` 签名并设置 `exp/iat` 与 `aud/iss/client_id`。 - 验证策略:资源端与授权端对请求对象进行验签与字段校验。 核心实现 请求对象生成与签名 ```ts type ReqObj = { iss: string; aud: string; client_id: string; redirect_uri: string; scope: string; response_type: string; iat: number; exp: number; state?: string; nonce?: string } function enc(s: string): Uint8Array { return new TextEncoder().encode(s) } function base64url(b: ArrayBuffer): string { const u = new Uint8Array(b); let s=''; for (let i=0;i { return crypto.subtle.importKey('pkcs8', pkcs8, type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', namedCurve:'P-256' }, false, ['sign']) } async function signReqObj(obj: ReqObj, key: CryptoKey, type: 'RS256'|'ES256'): Promise { const header = base64url(enc(JSON.stringify({ alg: type, typ: 'JWT' }))) const payload = base64url(enc(JSON.stringify(obj))) const sig = await crypto.subtle.sign(type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', hash:'SHA-256' }, key, enc(header + '.' + payload)) return header + '.' + payload + '.' + base64url(sig) } ``` 请求对象验证 ```ts async function importPublicKey(spki: ArrayBuffer, type: 'RS256'|'ES256'): Promise { return crypto.subtle.importKey('spki', spki, type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', namedCurve:'P-256' }, false, ['verify']) } function buf(s: string): ArrayBuffer { const b = atob(s.replace(/-/g,'+').replace(/_/g,'/')); const u = new Uint8Array(b.length); for (let i=0;i { const [h,p,s] = jwt.split('.') if (!h || !p || !s) return null const header = JSON.parse(new TextDecoder().decode(buf(h))) if (header.alg !== type) return null const ok = await crypto.subtle.verify(type==='RS256'?{ name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}:{ name:'ECDSA', hash:'SHA-256' }, pub, buf(s), enc(h + '.' + p)) if (!ok) return null const obj: ReqObj = JSON.parse(new TextDecoder().decode(buf(p))) const now = Math.floor(Date.now()/1000) if (obj.iss !== expected.iss || obj.aud !== expected.aud || obj.client_id !== expected.client_id) return null if (obj.exp + expected.leewaySec < now || obj.iat - expected.leewaySec > now) return null return obj } ``` 落地建议 - 推送授权参数并使用请求URI引用,客户端与服务端对请求对象进行验签与窗口校验。 - 对 `iss/aud/client_id/exp/iat/redirect_uri` 等字段进行严格校验,拒绝不一致或过期请求。 验证清单 - 请求对象是否成功验签且字段与时间窗口一致;PAR返回 `request_uri` 是否在有效期内。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部