如何在 WebAPI 2中实现 CORS 认证?

场景很简单,我需要从另一个服务器(不同于 API 服务器)登录来检索访问令牌。

我在 API 服务器上安装了 Microsoft.Owin.Cors

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

WebApiConfig.cs中,在 public static void Register(HttpConfiguration config)下,我添加了以下代码行:

// Cors
var cors = new EnableCorsAttribute("*", "*", "GET, POST, OPTIONS");
config.EnableCors(cors);

我还应该改变什么?

90607 次浏览

Look at what I have found!

Add in some custom headers inside <system.webServer>.

<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, DELETE" />
</customHeaders>
</httpProtocol>

Then I can do the CORS authentication.

I had many trial-and-errors to setup it for AngularJS-based web client.
For me, below approaches works with ASP.NET WebApi 2.2 and OAuth-based service.

  1. Install Microsoft.AspNet.WebApi.Cors nuget package.
  2. Install Microsoft.Owin.Cors nuget package.
  3. Add config.EnableCors(new EnableCorsAttribute("*", "*", "GET, POST, OPTIONS, PUT, DELETE")); to the above of WebApiConfig.Register(config); line at Startup.cs file.
  4. Add app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); to the Startup.Auth.cs file. This must be done prior to calling IAppBuilder.UseWebApi
  5. Remove any xml settings what Blaise did.

I found many setup variations and combinations at here stackoverflow or blog articles. So, Blaise's approach may or may not be wrong. It's just another settings I think.

To elaborate on Youngjae's answer, there's a great tutorial on setting up OWIN with Web API and enabling CORS in the process at http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

You'll want to add the NuGet package for CORS with the command:
Install-Package Microsoft.Owin.Cors -Version 2.1.0

Then add
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

to your Configuration method in Startup.cs so it looks something like:

public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}

web.config

<appSettings>
<add key="cors:Origins" value="*" />
<add key="cors:Headers" value="*" />
<add key="cors:Methods" value="GET, POST, OPTIONS, PUT, DELETE" />
</appSettings>

Startup.cs

var appSettings = WebConfigurationManager.AppSettings;


// If CORS settings are present in Web.config
if (!string.IsNullOrWhiteSpace(appSettings["cors:Origins"]))
{
// Load CORS settings from Web.config
var corsPolicy = new EnableCorsAttribute(
appSettings["cors:Origins"],
appSettings["cors:Headers"],
appSettings["cors:Methods"]);


// Enable CORS for ASP.NET Identity
app.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = request =>
request.Path.Value == "/token" ?
corsPolicy.GetCorsPolicyAsync(null, CancellationToken.None) :
Task.FromResult<CorsPolicy>(null)
}
});


// Enable CORS for Web API
config.EnableCors(corsPolicy);
}

Note: app.UserCors(...) should be called before configuring ASP.NET Identity.

Source: ASP.NET Web Application Starter Kit (ASP.NET Web API, Identity, SignalR)

The answer for me was found at

Web Api 2 Preflight CORS request for Bearer Token

Specifically, the /Token request using an implementation of OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials was adding the header in again. Add the OWIN CORS stuff before any other OWIN config, and remove the header from GrantResourceOwnerCredentials, as per that link, and voila. Good luck.

Adding customer Headers might not give you as much freedom in customize your security needs. It opens up all other part of the api to the world. The following code only does that for "token", and controller other part of api should be done via EableCors annotation.

public void ConfigureAuth(IAppBuilder app)
{
//other stuff
app.Use(async (context, next) =>
{
IOwinRequest req = context.Request;
IOwinResponse res = context.Response;
if (req.Path.StartsWithSegments(new PathString("/Token")))
{
var origin = req.Headers.Get("Origin");
if (!string.IsNullOrEmpty(origin))
{
res.Headers.Set("Access-Control-Allow-Origin", origin);
}
if (req.Method == "OPTIONS")
{
res.StatusCode = 200;
res.Headers.AppendCommaSeparatedValues("Access-Control-    Allow-Methods", "GET", "POST");
res.Headers.AppendCommaSeparatedValues("Access-Control-    Allow-Headers", "authorization", "content-type");
return;
}
}
await next();
});
//other stuff
}

To enable Cors, follow the instruction here.

You can find here the multiple ways you have to enable CORS at different scopes: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Anyway I had the same issue, and by adding the headers in different ways didn't get the complete solution.

I found out that the IIS uses handlers which overrides your CORS web app config if you don't specify the opposite.

In my case, I also had to remove the usage of IIS handlers by adding the following configuration at the main Web.config of my application:

<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>

Be aware that this configuration may be set by default when you create a new project depending on the type of it, but if you start from scratch you probably will have to add this config.

I just want to share my experience. I spent half of the day banging my head and trying to make it work. I read numerous of articles and SO questions and in the end I figured out what was wrong.

The line

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

was not the first one in Startup class Configuration method. When I moved it to the top - everything started working magically.

And no custom headers in web.config or config.EnableCors(corsPolicy); or anything else was necessary.

Hope this will help someone to save some time.

After many hours of searching and looking at many many different solutions to this i have managed to get this working as per the below.

There are a number of reasons this is happening. Most likely you have CORS enabled in the wrong place or it is enabled twice or not at all.

If you are using Web API and Owin Token end point then you need to remove all the references to CORS in your Web API method and add the correct owin method because web api cors will not work with Token endpoint whilst Owin cors will work for both Web API and Token auth end points so lets begin:

  1. Make sure you have the Owin Cors package installed Remove any line that you have eg.config.EnableCors(); from your WebAPIconfig.cs file

  2. Go to your startup.cs file and make sure you execute Owin Cors before any of the other configuration runs.

    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); ConfigureAuth(app);

  3. If your still having problems go to: Startup.Auth.cs and ensure you have the following in your ConfigureAuth method (you shouldnt need this if your startup.cs file is correct)

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

When using OWIN middleware to handle CORS, we do not need to add headers on the WebAPIConfig or the web.config file. Yes, adding the headers on the web.config file does work when you want public access, but if you need to limit the access based on a whitelist (domains), then allowing All access is no longer what you would like to do.

With OWINS, we can handle this by implementing this handler:

OAuthAuthorizationServerProvider.MatchEndpoint

With this handler, we can detect the request method (OPTIONS, POST...) and if the request should be treated as an Authorize or Token endpoint. This is the area where logic can be added to check the Origin header (request) and validate if this domain should be allowed by adding the response header Access-Control-Allow-Origin.

string origin = context.Request.Headers.Get("Origin");
var found = IsDomainAllowed(origin);
if (found){
context.Response.Headers.Add("Access-Control-Allow-Origin",
new string[] { origin });
}

For more background on this, look at this link: http://www.ozkary.com/2016/04/web-api-owin-cors-handling-no-access.html

Complete Soluction. You just need change some files, works for me.

Global.ascx

public class WebApiApplication : System.Web.HttpApplication {
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
} }

WebApiConfig.cs

All the request has call this code.

public static class WebApiConfig {
public static void Register(HttpConfiguration config)
{
EnableCrossSiteRequests(config);
AddRoutes(config);
}


private static void AddRoutes(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "Default",
routeTemplate: "api/{controller}/"
);
}


private static void EnableCrossSiteRequests(HttpConfiguration config)
{
var cors = new EnableCorsAttribute(
origins: "*",
headers: "*",
methods: "*");
config.EnableCors(cors);
} }

Some Controller

Nothing to change.

Web.config

You need add handlers in you web.config

<configuration>
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</configuration>