问题描述
我发誓这件事发生了很多次,以至于我实际上讨厌 CORS。
我将我的应用程序分为两部分,以便一个仅处理API方面的事务,另一个处理客户端的事务。
我之前已经做过,所以我知道我需要确保CORS已启用并允许所有操作,因此我在 WebApiConfig.cs
I swear this has happened so many times to me that I actually hate CORS.I have just split my application in two so that one handles just the API side of things and the other handles the client side stuff.I have done this before, so I knew that I needed to make sure CORS was enabled and allowed all, so I set this up in 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 }
);
}
如您所见,我的第一行启用了CORS,因此它应该可以工作。
如果我打开客户端应用程序并查询API,它确实可以工作(没有EnableCors,我会收到预期的CORS错误。
问题是我的 / token 仍然在现在,我知道/ token端点不是WebAPI的一部分,所以我创建了自己的OAuthProvider(必须指出的是,它可以在其他地方使用),如下所示:
As you can see, my first line Enables the CORS, so it should work.If I open my client application and query the API, it does indeed work (without the EnableCors I get the expected CORS error.The problem is my /token is still getting a CORS error. Now I am aware that /token endpoint is not part of the WebAPI, so I created my own OAuthProvider (which I must point out is being used in other places just fine) and that looks like this:
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);
}
}
如您所见,在 GrantResourceOwnerCredentials中方法,我再次允许CORS访问所有内容。这应该适用于所有对/ token的请求,但不是这样。
当我尝试从客户端应用程序登录时,出现CORS错误。
Chrome显示如下:
As you can see, In the GrantResourceOwnerCredentials method I enable CORS access to everything again. This should work for all requests to /token but it doesn't.When I try to login from my client application I get a CORS error.Chrome shows this:
,Firefox显示如下:
and Firefox shows this:
出于测试目的,我决定使用提琴手来看看是否可以看到其他任何东西。给我一个线索,关于正在发生的事情。当我尝试登录时,FIddler将响应代码显示为400,如果我查看原始响应,则会看到错误:
For testing purposes, I decided to use fiddler to see if I could see anything else that might give me a clue as to what is happening. When I try to login, FIddler shows a response code as 400 and if I look at the raw response I can see the error:
{"error":"unsupported_grant_type"}
这很奇怪,因为我发送的数据有不变,拆分前工作正常。
我决定在fiddler上使用Composer,并复制了我期望的POST请求。
当我执行它时,它可以正常工作,并且得到200的响应代码。
which is strange, because the data I am sending has not changed and was working fine before the split.I decided to use the Composer on fiddler and replicated what I expect the POST request to look like.When I Execute it, it works fine and I get a response code of 200.
有人知道为什么会发生这种情况吗?
Does anyone have any idea why this might be happening?
仅供参考,来自我的客户端应用程序的请求如下:
Just for reference, the request from my client app looks like this:
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
来自作曲家,看起来像这样:
from the composer, it looks like this:
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
推荐答案
事实证明,CORS根本没有问题。我有一个拦截器类,它错误地修改了标题。我建议其他人遇到这些问题,以备将来参考,如果您在WebConfig.cs或Startup类甚至是web.config中设置了CORS,那么您需要检查是否没有任何东西可以修改标头。如果是,请禁用它并再次测试。
It turns out that there was no issue with CORS at all. I had an interceptor class that was modifying the headers incorrectly. I suggest for future reference, anyone else having these issues, if you have your CORS set up either in WebConfig.cs or your Startup class or even the web.config then you need to check that nothing is modifying your headers. If it is, disable it and test again.
这篇关于WebAPI和令牌的严重CORS问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!