ASP.NET Core 3 React SPA集成ADFS认证时出现CORS错误
问题根源
你的错误本质是认证重定向流与AJAX请求的冲突:当React SPA通过AJAX调用需要认证的后端API时,ASP.NET Core默认返回302重定向到ADFS,但浏览器会把重定向后的ADFS请求视为跨域AJAX请求——而ADFS服务器不会返回Access-Control-Allow-Origin头,因此触发CORS拦截。
SAML/OIDC/WsFed这类认证协议都是基于浏览器页面重定向设计的,并非为AJAX请求准备,所以你之前配置的后端CORS规则完全不会生效(CORS是后端返回给前端的响应头,而报错中的请求是直接发往ADFS的)。
解决方案
方案1:让后端对AJAX请求返回401而非302
修改ASP.NET Core的认证配置,识别AJAX请求(通过请求头判断),返回401状态码而非302重定向,再由前端捕获401后手动触发页面跳转去ADFS认证。
在Startup.cs的认证配置中添加:
services.AddAuthentication() .AddSaml2(options => { // 你的SAML配置内容 }) .AddCookie(options => { options.Events.OnRedirectToLogin = context => { // 判断是否为AJAX请求 bool isAjaxRequest = context.Request.Headers["X-Requested-With"] == "XMLHttpRequest" || context.Request.Headers["Accept"].Contains("application/json"); if (isAjaxRequest) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.CompletedTask; } // 非AJAX请求正常执行重定向 context.Response.Redirect(context.RedirectUri); return Task.CompletedTask; }; });
然后在React前端给API请求添加拦截器,捕获401时跳转至认证端点:
// 以Axios为例 axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { window.location.href = '/login'; // 触发ADFS重定向流程 } return Promise.reject(error); } );
方案2:前端主动发起认证跳转
在SPA初始化时,先调用一个无需认证的检查接口(比如/api/auth/check)判断用户是否已认证,若未认证则直接跳转至/login端点,由ASP.NET Core处理ADFS重定向,避免通过AJAX调用需认证接口触发重定向。
额外注意:避免SPA路由拦截认证端点
因为使用UseSpa托管React,需确保/login这类认证端点不会被SPA路由捕获,在Startup.cs的中间件配置中,要先配置认证与接口路由,再加载SPA:
app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 配置认证触发端点 endpoints.Map("/login", context => { return context.ChallengeAsync(Saml2Defaults.AuthenticationScheme); }); }); app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; // 你的SPA配置内容 });
为什么之前的CORS配置无效?
你配置的CORS规则是针对你的后端API的响应头,而报错中的请求是浏览器直接发送给ADFS服务器的,ADFS不会返回CORS头——你无权修改ADFS配置,所以后端的CORS规则对这个请求完全不起作用。
内容的提问来源于stack exchange,提问作者tomer vainer

