---

title: CORS细粒度策略与预检缓存最佳实践

keywords:

  • CORS
  • 预检
  • Vary
  • Allow-Credentials
  • Allow-Methods
  • Allow-Headers
  • Max-Age
  • 精确白名单
  • 路由维度
  • 预检缓存

description: 构建精确白名单与路由维度的CORS策略,正确处理预检与缓存(Vary与Max-Age),避免凭证与通配冲突,附服务端中间件与验收清单。

categories:

  • 文章资讯
  • 编程技术

---

一、目标与约束

  • 精确来源白名单;凭证模式下禁止*;按路由/方法/头维度控制。
  • 预检响应必须设置Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers与合理Max-Age≤600

二、策略定义

type CorsRule = { origin: string; routes: Record<string, { methods: string[]; headers: string[]; credentials: boolean }> }
const rules: CorsRule[] = [
  { origin: 'https://example.com', routes: { '/api/user': { methods: ['GET','POST'], headers: ['Content-Type','Authorization'], credentials: true } } }
]

三、匹配与响应

type Req = { method: string; path: string; headers: Record<string, string | undefined> }
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')
}

四、预检处理

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()
}

五、整合与验收

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≤600Vary包含请求方法与头;简单请求返回允许来源与可选凭证。
  • 审计包含来源、路由与方法;拒绝时返回明确错误码与原因。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部