一、目标与约束
- 精确来源白名单;凭证模式下禁止`*`;按路由/方法/头维度控制。
- 预检响应必须设置`Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers`与合理`Max-Age≤600`。
二、策略定义
```ts
type CorsRule = { origin: string; routes: Record }
const rules: CorsRule[] = [
{ origin: 'https://example.com', routes: { '/api/user': { methods: ['GET','POST'], headers: ['Content-Type','Authorization'], credentials: true } } }
]
```
三、匹配与响应
```ts
type Req = { method: string; path: string; headers: Record }
type Res = { setHeader: (k: string, v: string) => void; status: (n: number) => Res; end: (b?: string) => void }
function matchRule(origin: string, path: string): { methods: string[]; headers: string[]; credentials: boolean } | null {
const r = rules.find(x => x.origin === origin)
if (!r) return null
const conf = r.routes[path]
return conf || null
}
function applySimple(req: Req, res: Res) {
const origin = req.headers['origin'] || ''
const conf = matchRule(origin, req.path)
if (!conf) return
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Vary', 'Origin')
if (conf.credentials) res.setHeader('Access-Control-Allow-Credentials', 'true')
}
```
四、预检处理
```ts
function preflight(req: Req, res: Res) {
const origin = req.headers['origin'] || ''
const method = req.headers['access-control-request-method'] || ''
const hdrs = (req.headers['access-control-request-headers'] || '').split(',').map(s => s.trim()).filter(Boolean)
const conf = matchRule(origin, req.path)
if (!conf) return res.status(403).end('cors_forbidden')
if (!conf.methods.includes(method)) return res.status(405).end('method_not_allowed')
for (const h of hdrs) if (!conf.headers.map(x => x.toLowerCase()).includes(h.toLowerCase())) return res.status(400).end('header_not_allowed')
res.setHeader('Access-Control-Allow-Origin', origin)
res.setHeader('Access-Control-Allow-Methods', conf.methods.join(','))
res.setHeader('Access-Control-Allow-Headers', conf.headers.join(','))
res.setHeader('Access-Control-Max-Age', '600')
if (conf.credentials) res.setHeader('Access-Control-Allow-Credentials', 'true')
res.setHeader('Vary', 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers')
return res.status(204).end()
}
```
五、整合与验收
```ts
function corsMiddleware(req: Req, res: Res, next: Function) {
const origin = req.headers['origin'] || ''
const conf = matchRule(origin, req.path)
if (!conf) return next()
if (req.method === 'OPTIONS') return preflight(req, res)
applySimple(req, res)
next()
}
```
- 白名单来源精确匹配;凭证模式不使用`*`;路由/方法/头严格控制。
- 预检缓存`Max-Age≤600`且`Vary`包含请求方法与头;简单请求返回允许来源与可选凭证。
- 审计包含来源、路由与方法;拒绝时返回明确错误码与原因。
发表评论 取消回复