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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions