WebAPI和令牌的可怕CORS问题

r3p*_*ica 5 asp.net cors asp.net-web-api

我发誓这已经发生了很多次,我真的讨厌 CORS.我刚刚将我的应用程序拆分为两个,这样一个处理事物的API方面,另一个处理客户端的东西.我以前做过这个,所以我知道我需要确保CORS已启用并允许所有,所以我在WebApiConfig.cs中设置它

public static void Register(HttpConfiguration config)
{

    // Enable CORS
    config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

    // Web API configuration and services
    var formatters = config.Formatters;
    var jsonFormatter = formatters.JsonFormatter;
    var serializerSettings = jsonFormatter.SerializerSettings;

    // Remove XML formatting
    formatters.Remove(config.Formatters.XmlFormatter);
    jsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));

    // Configure our JSON output
    serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    serializerSettings.Formatting = Formatting.Indented;
    serializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    serializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.None;

    // Configure the API route
    config.MapHttpAttributeRoutes();
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我的第一行启用了CORS,因此它应该可以工作.如果我打开我的客户端应用程序并查询API,它确实有效(没有EnableCors我得到了预期的CORS错误.问题是我的/令牌仍然出现CORS错误.现在我知道/令牌端点不是WebAPI,所以我创建了自己的OAuthProvider(我必须指出它在其他地方使用得很好),看起来像这样:

public class OAuthProvider<TUser> : OAuthAuthorizationServerProvider
    where TUser : class, IUser
{
    private readonly string publicClientId;
    private readonly UserService<TUser> userService;

    public OAuthProvider(string publicClientId, UserService<TUser> userService)
    {
        if (publicClientId == null)
            throw new ArgumentNullException("publicClientId");

        if (userService == null)
            throw new ArgumentNullException("userService");

        this.publicClientId = publicClientId; 
        this.userService = userService;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        var user = await this.userService.FindByUserNameAsync(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "The user name or password is incorrect.");
            return;
        }

        var oAuthIdentity = this.userService.CreateIdentity(user, context.Options.AuthenticationType);
        var cookiesIdentity = this.userService.CreateIdentity(user, CookieAuthenticationDefaults.AuthenticationType);
        var properties = CreateProperties(user.UserName);
        var ticket = new AuthenticationTicket(oAuthIdentity, properties);

        context.Validated(ticket);
        context.Request.Context.Authentication.SignIn(cookiesIdentity);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
            context.AdditionalResponseParameters.Add(property.Key, property.Value);

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        // Resource owner password credentials does not provide a client ID.
        if (context.ClientId == null)
        {
            context.Validated();
        }

        return Task.FromResult<object>(null);
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (context.ClientId == this.publicClientId)
        {
            var redirectUri = new Uri(context.RedirectUri);
            var expectedRootUri = new Uri(context.Request.Uri, redirectUri.PathAndQuery);

            if (expectedRootUri.AbsoluteUri == redirectUri.AbsoluteUri)
                context.Validated();
        }

        return Task.FromResult<object>(null);
    }

    public static AuthenticationProperties CreateProperties(string userName)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
        {
            { "userName", userName }
        };

        return new AuthenticationProperties(data);
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,在GrantResourceOwnerCredentials方法中,我再次启用CORS访问所有内容.这应该适用于/ token的所有请求,但它不适用.当我尝试从我的客户端应用程序登录时,我收到一个CORS错误.Chrome显示了这一点:

XMLHttpRequest无法加载http:// localhost:62605/token.对预检请求的响应未通过访问控制检查:请求的资源上不存在"Access-Control-Allow-Origin"标头.因此不允许来源" http:// localhost:50098 ".响应具有HTTP状态代码400.

和Firefox显示这个:

跨源请求已阻止:同源策略禁止在http:// localhost:62605/token处读取远程资源.(原因:缺少CORS标题'Access-Control-Allow-Origin').跨源请求已阻止:同源策略禁止在http:// localhost:62605/token处读取远程资源.(原因:CORS请求失败).

出于测试目的,我决定使用fiddler来查看是否可以看到任何其他可能让我知道发生了什么的线索.当我尝试登录时,FIddler显示响应代码为400,如果我查看原始响应,我可以看到错误:

{"error":"unsupported_grant_type"}
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为我发送的数据没有改变,并且在拆分之前工作正常.我决定在fiddler上使用Composer并复制我期望POST请求的样子.当我执行它时,它工作正常,我得到200的响应代码.

有谁知道为什么会发生这种情况?

更新1

仅供参考,我的客户端应用程序的请求如下所示:

OPTIONS http://localhost:62605/token HTTP/1.1
Host: localhost:62605
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: http://localhost:50098
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36
Access-Control-Request-Headers: accept, authorization, content-type
Accept: */*
Referer: http://localhost:50098/account/signin
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Run Code Online (Sandbox Code Playgroud)

来自作曲家,它看起来像这样:

POST http://localhost:62605/token HTTP/1.1
User-Agent: Fiddler
Content-Type: 'application/x-www-form-urlencoded'
Host: localhost:62605
Content-Length: 67

grant_type=password&userName=foo&password=bar
Run Code Online (Sandbox Code Playgroud)

Bil*_*one 7

代替

 public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
Run Code Online (Sandbox Code Playgroud)

摆脱这个:

 context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
Run Code Online (Sandbox Code Playgroud)

目前你正在两次做CORS事情.一次使用.EnableCors,再次通过在令牌端点中写入标头.

值得一提的是,在我的OWIN创业课程中,我在最顶层有这个:

app.UseCors(CorsOptions.AllowAll);
Run Code Online (Sandbox Code Playgroud)

我也没有在我的WebAPI寄存器方法中,因为我让OWIN启动处理它.

  • 我从一个角度应用程序调用ac#web api遇到了这个问题.移动**app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);**到**public void Configuration(IAppBuilder app){}**的顶部就可以了.谢谢比尔. (2认同)

r3p*_*ica 1

事实证明CORS根本没有问题。我有一个拦截器类错误地修改了标头。我建议任何其他遇到这些问题的人在将来参考,如果您在 WebConfig.cs 或 Startup 类甚至 web.config 中设置了 CORS,那么您需要检查是否没有任何内容修改您的标头。如果是,请将其禁用并再次测试。

  • 愿意详细说明吗? (4认同)