为啥 CORS 错误“对预检请求的响应未通过访问控制检查”?

Posted

技术标签:

【中文标题】为啥 CORS 错误“对预检请求的响应未通过访问控制检查”?【英文标题】:Why CORS error "Response to preflight request doesn't pass access control check"?为什么 CORS 错误“对预检请求的响应未通过访问控制检查”? 【发布时间】:2019-03-31 17:32:46 【问题描述】:

我在我的项目中使用 具有 OAuth 授权的 ASP.NET Web API

我尝试使用最佳实践将解决方案中的每一层解耦。 我有一个包含 AngularJS 文件和其他资源的 web 项目,这些文件和其他资源上传到 www.example.com,我还有另一个项目,它是受保护的后端 web api 控制器和服务器端的东西,上传到 api.example.com

在 localhost 中一切正常。当我将此发布到生产服务器时,对“/token”的请求成功,但在后端 api 中的任何控制器中请求任何操作都会返回此错误: “从源 'http://www.example.com' 访问 XMLHttpRequest 在 'http://api.example.com/someRoute' 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:没有 'Access-Control-Allow-Origin' 标头存在于请求的资源上。”。

我在互联网上搜索了几乎所有活动链接以查找类似错误,但我还没有答案!

我从后端 API 中粘贴了一些代码,以便您更好地理解我的方法。

public partial class Startup

    public void Configuration(IAppBuilder app)
    

        HttpConfiguration httpConfig = new HttpConfiguration();

        UnityConfig.Register(httpConfig);

        ConfigureAuth(app);

        WebApiConfig.Register(httpConfig);

        app.UseWebApi(httpConfig);

        #region AutoMapper Init
        DtoMapping.Map();
        #endregion
    



public void ConfigureAuth(IAppBuilder app)
    
        // Configure the auth context, user manager and signin manager to use a single instance per request
        app.CreatePerOwinContext(AuthContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        
            //remove this line on production
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new SimpleAuthorizationServerProvider()
        ;

        // Token Generation
        app.UseOAuthBearerTokens(OAuthServerOptions);
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    

public class WebApiConfig

    public static void Register(HttpConfiguration config)
    
        EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(cors);

        config.MapHttpAttributeRoutes();

        //...
    


[EnableCors("*", "*", "*")]
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    
        context.Validated();
        return Task.FromResult<object>(null);
    

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    
        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[]  "*" );

        IList<string> roleNames;
        using (var _repo = new IdentityRepository())
        
            var user = await _repo.FindUserAsync(context.UserName, context.Password);
            if (user == null)
            
                context.SetError("invalid_grant", "username or password is invalid.");
                context.Rejected();
                return;
            

            roleNames = await _repo.GetRolesForUserAsync(user.Id);
        

        identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
        foreach (var roleName in roleNames)
        
            identity.AddClaim(new Claim(ClaimTypes.Role, roleName));
        

        var props = new AuthenticationProperties(new Dictionary<string, string>
            
                
                    "userName", context.UserName
                
            );

        var ticket = new AuthenticationTicket(identity, props);
        context.Validated(ticket);

    

那么,谁能帮帮我?

感谢您的宝贵时间。

【问题讨论】:

我认为它对您不起作用,因为您在 WebApiConfig 中全局设置了“Cors”,并在SimpleAuthorizationServerProvider 中附加了属性。尝试删除SimpleAuthorizationServerProvider 中的属性,然后重试。据我所知,我遇到了同样的问题,这对我有用。 另外,我建议只为 DEBUG (/Development) 设置 CORS,而不是为发布设置。这是一种不应始终禁用的安全机制。使用子域,您应该至少为子域设置 cors 而不是通配符。 感谢您的回答,但删除属性并不能解决问题。我认为“/token”用于生成访问令牌的唯一原因是授权提供程序类上可用的此属性。如果我删除它,对“/token”的 POST 请求会返回与请求任何其他路由相同的 cors 错误,因此登录过程将不起作用。我什至尝试将此属性放在 web api 控制器上,看看它是否有效,尽管使用了全局 cors 设置,但结果仍然相同。 出于您提到的安全原因,我肯定会删除星号并将 cors 限制在生产中的某些域中,但现在我只需要看看它是否适用于任何设置。 【参考方案1】:

如果有人想知道,这就是我解决这个问题的方法:

这不是关于不在 asp.net web api 中设置 cors 策略(正如我已经提到的那样,我做了Microsoft docs website 的建议。)

问题在于 IIS 未配置为处理 OPTION 动词方法! 并且因为飞行前请求使用了 OPTION 方法,所以总是找不到 404(来自网络服务器的拒绝),因此出现错误。

我应该提一下,直到今天我仍然不知道为什么“/token”在配置 Web 服务器之前起作用,为什么控制器没有相同的反应!

但无论如何,问题就这样解决了。 希望对您有所帮助!

【讨论】:

【参考方案2】:

可能有点旧,但值得一提的是,您需要在 global.asax 中添加 cors 标头

protected void Application_BeginRequest(object sender, EventArgs e)

    var context = System.Web.HttpContext.Current;
    var origins = System.Configuration.ConfigurationManager.AppSettings["AllowedCorsDomain"]?.TrimEnd('/');
    context.Response.AddHeader("Access-Control-Allow-Origin", origins);

    if (context.Request.HttpMethod == "OPTIONS")
    
        //These headers are handling the "pre-flight" OPTIONS call sent by the browser
        context.Response.AddHeader("Access-Control-Allow-credentials", "true");
        context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, POST, DELETE, OPTIONS");
        context.Response.AddHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type, origin, authorization, Accepts, accept, client-security-token, access-control-allow-headers");
        context.Response.AddHeader("Access-Control-Max-Age", "86400");
        context.Response.End();
    

【讨论】:

谢谢。 CORS 一直很繁琐。因为我只需要它用于 DEV,所以我将它包装在 #if DEBUG 中并继续我的工作。我必须对 Windows 身份验证进行的一项更改是将 Access-Control-Allow-credentials 移到条件之外。

以上是关于为啥 CORS 错误“对预检请求的响应未通过访问控制检查”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到 CORS 错误原因:CORS 请求未成功

为啥只有 Angular 才会出现 CORS 错误? [复制]

为啥一些跨域 XMLHttpRequest 请求不返回 CORS 错误

为啥调用 API 时出现 CORS 错误

为啥我在角度请求中收到此错误? (CORS)

仅当查询字符串不同时,为啥会出现 CORS 错误