---
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。 - 验证策略:资源端与授权端对请求对象进行验签与字段校验。
核心实现
请求对象生成与签名
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<u.length;i++) s+=String.fromCharCode(u[i]); return btoa(s).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'') }
async function importPrivateKey(pkcs8: ArrayBuffer, type: 'RS256'|'ES256'): Promise<CryptoKey> { 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<string> {
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)
}
请求对象验证
async function importPublicKey(spki: ArrayBuffer, type: 'RS256'|'ES256'): Promise<CryptoKey> { 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<b.length;i++) u[i]=b.charCodeAt(i); return u.buffer }
async function verifyReqObj(jwt: string, pub: CryptoKey, type: 'RS256'|'ES256', expected: { iss: string; aud: string; client_id: string; leewaySec: number }): Promise<ReqObj | null> {
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是否在有效期内。

发表评论 取消回复