---
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` 是否在有效期内。
发表评论 取消回复