Skip to content

fix(backend): 放行受保护接口的 CORS 预检请求 #184

Description

@huyanxius

问题

当前后端已挂载 CORSMiddleware,但受保护接口的跨域预检请求仍会被 AuthMiddleware 提前拦截。

浏览器发送预检 OPTIONS 时不会携带 Bearer token。后端因此返回 HTTP 200 + 业务码 401,且响应缺少 access-control-allow-* 头,浏览器不会继续发送实际的 GET、PATCH 或 POST 请求。

这会阻断所有前后端分域部署下的受保护接口,不只影响账号中心。

复现证据

在最新 mainefd230e)配置:

WINDUP_CORS_ORIGINS=https://frontend.example

实际中间件顺序:

RateLimitMiddleware → AuthMiddleware → CORSMiddleware

Preflight Result
POST /auth/login 正常返回 OK,包含允许来源、方法和请求头
GET /auth/me 返回业务码 401,缺少全部 CORS 响应头
PATCH /auth/profile 返回业务码 401,缺少全部 CORS 响应头
POST /auth/change-password 返回业务码 401,缺少全部 CORS 响应头
GET /projects 返回业务码 401,缺少全部 CORS 响应头

根因

create_app() 先注册 CORSMiddleware,随后注册 AuthMiddlewareRateLimitMiddleware。按当前 Starlette 中间件装配顺序,后注册的中间件先收到请求,因此鉴权发生在 CORS 之前。

AuthMiddleware 只按路径白名单放行请求,没有单独放行 OPTIONS。受保护路径的预检没有 Authorization header,于是在到达 CORS 中间件前被返回“未登录”。

该顺序随 #149 的认证中间件注册进入主线。#139 处理的是“完全未挂 CORS”,#143 处理的是另一分支上的来源配置问题,均未覆盖受保护路径的预检顺序。

预期行为

  • 已配置来源发出的合法预检请求应由 CORS 层处理,不要求 Bearer token。
  • 预检成功后,实际受保护请求仍必须通过现有 JWT 鉴权。
  • 未配置的来源不得获得 access-control-allow-origin
  • 公共认证接口行为保持不变。

验收标准

  • GETPATCHPOST 类型的受保护接口预检均能正常返回 CORS 响应头。
  • 预检请求不要求 Authorization header。
  • 不带 token 的实际受保护请求仍返回现有业务码 401。
  • 未配置来源仍无法通过预检。
  • 增加自动化回归测试,同时覆盖公共接口与受保护接口的预检差异。
  • 浏览器可跨域调用 /auth/me/auth/profile/auth/change-password/projects

Scope

  • 本 Issue 只修复 CORS 与鉴权中间件的请求顺序或预检放行逻辑,并补充测试。
  • 不调整 JWT、业务权限、允许来源策略或前端账号中心代码。

Related Issues

Refs #139
Refs #143
Refs #149
Refs #161
Refs #183

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions