ASP.NET Core 6+ CORS 配置必须用 WithOrigins 指定完整协议+域名+端口,禁用通配符;需用 SetIsOriginAllowed 支持子域或动态白名单;UseCors 必须在 UseAuthentication 后、UseAuthorization 前;AllowCredentials 与 AllowAnyOrigin 冲突会导致启动失败。

ASP.NET Core 6+ 默认 CORS 中间件怎么配域名白名单
默认的 AddCors + UseCors 能满足大多数场景,但容易误配成全放开(比如用 "*" 允许任意源),实际生产中必须限制具体域名。关键不是“加中间件”,而是策略定义时别漏掉 WithOrigins 和严格匹配逻辑。
-
WithOrigins("https://a.example.com", "https://b.example.com")—— 必须写完整协议+域名+端口(如https://localhost:5173),不支持通配符子域(https://*.example.com不生效) - 如果前端是
file://协议(本地双击 HTML 测试),浏览器会把 origin 设为null,而WithOrigins不接受null,此时需改用SetIsOriginAllowed自定义判断 - 调用
WithOrigins后,AllowAnyOrigin()会被自动忽略 —— 两者不能共存,否则启动报错InvalidOperationException: When setting up CORS, you must not mix calls to WithOrigins and AllowAnyOrigin
如何用 SetIsOriginAllowed 精确控制动态域名
当需要支持子域(如 app1.example.com、app2.example.com)、或从数据库/配置中心加载白名单时,SetIsOriginAllowed 是唯一可靠方式。它接收一个 Func<string bool></string>,在每次预检(OPTIONS)请求时执行。
- 不要在 lambda 里做耗时操作(如查 DB),否则阻塞请求;建议提前加载到内存字典,再用
origin.EndsWith(".example.com")或正则匹配 - 注意协议和端口:浏览器发来的
Origin头是完整字符串(如https://admin.example.com:8080),匹配前建议先用Uri.TryCreate解析,避免被恶意构造的 URL 绕过 - 示例:
services.AddCors(options => { options.AddPolicy("DynamicPolicy", builder => { builder.SetIsOriginAllowed(origin => { if (!Uri.TryCreate(origin, UriKind.Absolute, out var uri)) return false; return uri.Host.EndsWith(".example.com") && uri.Scheme == "https"; }) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); // 注意:开启后不能用 AllowAnyOrigin,且 WithOrigins 也不支持 credentials }); });
为什么 UseCors 要放在 UseAuthentication 之后、UseAuthorization 之前
CORS 预检请求(OPTIONS)不带身份凭证(cookie / Authorization header),所以中间件顺序错了会导致 OPTIONS 被认证/鉴权中间件拦截并返回 401/403,前端永远收不到 200 OK 的预检响应。
- 正确顺序:
UseRouting→UseAuthentication→UseCors→UseAuthorization→UseEndpoints - 如果用了
[EnableCors]特性作用于某个 Controller,也要确保全局UseCors已注册,否则特性无效 - 调试技巧:用 curl 发送预检请求观察响应头:
curl -H "Origin: https://test.com" -H "Access-Control-Request-Method: POST" -X OPTIONS https://api.example.com/weatherforecast -I,检查是否返回Access-Control-Allow-Origin
AllowCredentials 和 WithOrigins 冲突时的实际表现
这是最容易翻车的点:只要调用了 AllowCredentials(),就绝对不能用 AllowAnyOrigin(),也不能在 WithOrigins 里传空数组或 null —— 否则中间件直接抛异常,应用启动失败。
- 若必须支持凭据(如 cookie 登录态),又想兼容多个域名,只能走
SetIsOriginAllowed,并在函数里返回true的同时确保 origin 非空、协议合法 - 浏览器对
credentials: true的请求有额外限制:响应头Access-Control-Allow-Origin不能是*,必须是精确匹配的 origin 字符串(如https://a.example.com),这也是为什么WithOrigins内部会做字符串比对而非模糊匹配 - 开发时容易忽略:Vue/Vanilla JS 的
fetch默认不带 credentials,要显式写{ credentials: 'include' },否则后端开了AllowCredentials也白搭


















