带有 Windows 身份验证的 WebAPI CORS - 允许匿名 OPTIONS 请求
Posted
技术标签:
【中文标题】带有 Windows 身份验证的 WebAPI CORS - 允许匿名 OPTIONS 请求【英文标题】:WebAPI CORS with Windows Authentication - allow Anonymous OPTIONS request 【发布时间】:2015-02-09 10:38:12 【问题描述】:我有一个使用 Windows 身份验证运行的 WebAPI 2 REST 服务。它与网站分开托管,因此我使用 ASP.NET CORS NuGet 包启用了 CORS。我的客户网站正在使用 AngularJS。
到目前为止,这是我所经历的:
-
我没有设置 withCredentials,因此 CORS 请求返回 401。通过将 withCredentials 添加到我的 $httpProvider 配置中解决。
接下来,我使用通配符来源设置了 EnableCorsAttribute,这在使用凭据时是不允许的。通过设置明确的来源列表来解决。
这使我的 GET 请求成功,但我的 POST 发出了预检请求,并且我没有创建任何控制器操作来支持 OPTIONS 动词。为了解决这个问题,我将 MessageHandler 实现为全局 OPTIONS 处理程序。它只是为任何 OPTIONS 请求返回 200。我知道这并不完美,但目前在 Fiddler 中有效。
我被困在哪里 - 我的 Angular 预检调用不包括凭据。根据this answer,这是设计使然,因为 OPTIONS 请求被设计为匿名的。但是,Windows 身份验证使用 401 停止请求。
我尝试将 [AllowAnonymous] 属性放在我的 MessageHandler 上。在我的开发计算机上,它可以工作 - OPTIONS 动词不需要身份验证,但其他动词需要。但是,当我构建并部署到测试服务器时,我的 OPTIONS 请求继续收到 401。
在使用 Windows 身份验证时,是否可以在我的 MessageHandler 上应用 [AllowAnonymous]?如果是这样,有关如何执行此操作的任何指导?或者这是错误的兔子洞,我应该寻找不同的方法?
更新: 我能够通过在 IIS 中的站点上同时设置 Windows 身份验证和匿名身份验证来使其工作。这导致一切都允许匿名,所以我添加了一个全局过滤器 Authorize,同时在我的 MessageHandler 上保留 AllowAnonymous。
但是,这感觉像是一种 hack……我一直都明白,应该只使用一种身份验证方法(不能混合使用)。如果有人有更好的方法,我将不胜感激。
【问题讨论】:
您可能应该添加诸如“selfhost”或“owin”之类的标签,因为这与 IIS 之类的东西无关。 :) 我使用了本指南codeproject.com/Articles/1119206/…(2016 年发布),与下面的大多数答案非常相似 【参考方案1】:我通过 HttpListener 使用自托管,以下解决方案对我有用:
-
我允许匿名 OPTIONS 请求
在 SupportsCredentials 设置为 true 的情况下启用 CORS
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
var listener = appBuilder.Properties["System.Net.HttpListener"] as HttpListener;
if (listener != null)
listener.AuthenticationSchemeSelectorDelegate = (request) =>
if (String.Compare(request.HttpMethod, "OPTIONS", true) == 0)
return AuthenticationSchemes.Anonymous;
else
return AuthenticationSchemes.IntegratedWindowsAuthentication;
;
【讨论】:
这应该被标记为答案,AuthenticationSchemeSelectorDelegate 正是您需要确保 OPTIONS 请求被全局排除在任何身份验证设置之外。太棒了,这个答案也正是我所需要的。 (我建议将“selfhost”标签添加到问题中)。 如何在 webApiConfig.cs 注册方法中获取 appBuilder 实例? 这个答案的主要问题是问题直接提到他正在使用 IIS。 (我知道它可能是后来添加的,但仍然。更新了问题标签...)【参考方案2】:我一直在努力使 CORS 请求在以下限制内工作(与 OP 的非常相似):
所有用户的 Windows 身份验证 不允许匿名身份验证 与 IE11 一起工作,in some cases 不发送 CORS 预检请求(或至少不作为 OPTIONS 请求到达 global.asax BeginRequest)我的最终配置如下:
web.config - 允许未经身份验证(匿名)的预检请求 (OPTIONS)
<system.web>
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*"/>
<deny users="?" />
</authorization>
</system.web>
global.asax.cs - 正确回复允许来自另一个域的调用者接收数据的标头
protected void Application_AuthenticateRequest(object sender, EventArgs e)
if (Context.Request.HttpMethod == "OPTIONS")
if (Context.Request.Headers["Origin"] != null)
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Response.End();
CORS 启用
public static class WebApiConfig
public static void Register(HttpConfiguration config)
// all requests are enabled in this example. SupportsCredentials must be here to allow authenticated requests
var corsAttr = new EnableCorsAttribute("*", "*", "*") SupportsCredentials = true ;
config.EnableCors(corsAttr);
protected void Application_Start()
GlobalConfiguration.Configure(WebApiConfig.Register);
【讨论】:
这有帮助!我最终没有将授权元素添加到 web.config 中,它仍然有效。我相信 Application_AuthenticateRequest 覆盖消除了在调用 OPTION 方法时允许进行身份验证的需要。 @MadMoai - 我会在没有授权的情况下尝试,尽管我认为在 IIS 中仅启用 Windows 身份验证并且必须为未经身份验证的请求启用选项时需要它。谢谢。 根据Microsoft,CORS 规范指出,如果 SupportsCredentials 为 true,则将来源设置为“*”是无效的。 @WorkSmarter 这是真的,但事实证明,WebAPI 2 (Microsoft.AspNet.WebApi.Cors.5.2.7
) 的当前版本的 CORS 库不会使用此设置返回“Access-Control-Allow-Origin: *”,而是使用请求的 Origin 标头中的值...【参考方案3】:
这是一个更简单的解决方案——几行代码允许所有“OPTIONS”请求有效地模拟应用程序池帐户。您可以关闭匿名,并按照常规做法配置 CORS 策略,然后将以下内容添加到您的 global.asax.cs:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
if (Context.Request.HttpMethod == "OPTIONS" && Context.User == null)
Context.User = System.Security.Principal.WindowsPrincipal.Current;
【讨论】:
【参考方案4】:在我们的情况下:
Windows 身份验证 多个 CORS 来源 SupportCredentials 设置为 true IIS 托管我们发现解决方案在别处:
在 Web.Config 中,我们只需添加 runAllManagedModulesForAllRequests=true
<modules runAllManagedModulesForAllRequests="true">
我们通过研究为什么没有触发 Application_BeginRequest 的解决方案最终得出了这个解决方案。
我们拥有的其他配置:
在Web.Config
中 <authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="*" />
<deny users="?"/>
</authorization>
在 WebApiConfig
private static string GetAllowedOrigins()
return ConfigurationManager.AppSettings["CorsOriginsKey"];
public static void Register(HttpConfiguration config)
//set cors origins
string origins = GetAllowedOrigins();
var cors = new EnableCorsAttribute(origins, "*", "*");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/action/id",
defaults: new id = RouteParameter.Optional
);
BTW "*" cors origin 与 Windows Authentication / SupportCredentials = true 不兼容
https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#pass-credentials-in-cross-origin-requests
【讨论】:
【参考方案5】:我以非常相似的方式解决了它,但有一些细节并专注于 oData 服务
我没有在 IIS 中禁用匿名身份验证,因为我需要它来发布请求
我在 Global.aspx 中添加了与上面相同的代码(在 Access-Control-Allow-Headers
中添加 MaxDataServiceVersion
)
protected void Application_BeginRequest(object sender, EventArgs e)
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,MaxDataServiceVersion");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
和WebAPIConfig.cs
public static void Register(HttpConfiguration config)
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
和 AngularJS 调用
$http(
method: 'POST',
url: 'http://XX.XXX.XXX.XX/oData/myoDataWS.svc/entityName',
withCredentials: true,
headers:
'Content-Type': 'application/json;odata=verbose',
'Accept': 'application/json;odata=light;q=1,application/json;odata=verbose;q=0.5',
'MaxDataServiceVersion': '3.0'
,
data:
'@odata.type':'entityName',
'field1': 1560,
'field2': 24,
'field3': 'sjhdjshdjsd',
'field4':'wewewew',
'field5':'ewewewe',
'lastModifiedDate':'2015-10-26T11:45:00',
'field6':'1359',
'field7':'5'
);
【讨论】:
【参考方案6】:戴夫,
在玩弄了 CORS 包之后,这就是它对我有用的原因:[EnableCors(origins: "", headers: "", methods: "*", SupportsCredentials=true)]
我必须启用 SupportsCredentials=true。 Origins、Headers 和 Methods 都设置为“*”
【讨论】:
我从一开始就有这个设置,否则服务器不会接受凭据头。不过,感谢您的尝试。 asp.net/web-api/overview/security/… 部分:在跨域请求中传递凭据 CORS 规范还规定,如果 SupportsCredentials 为 true,则将源设置为“”是无效的。您应该提供您想要允许的来源,而不是使用“”【参考方案7】:如果不需要,请在 IIS 中禁用匿名身份验证。
在你的全局 asax 中添加这个:
protected void Application_BeginRequest(object sender, EventArgs e)
if ((Context.Request.Path.Contains("api/") || Context.Request.Path.Contains("odata/")) && Context.Request.HttpMethod == "OPTIONS")
Context.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
Context.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
Context.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
Context.Response.AddHeader("Access-Control-Allow-Credentials", "true");
Context.Response.End();
确保您在启用 cors 的位置也启用了凭证使用,例如:
public static void Register(HttpConfiguration config)
// Web API configuration and services
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
如您所见,我在全局范围内启用了 CORS,并使用应用程序 BeginRequest 挂钩对 api (Web Api) 的所有 OPTIONS 请求和 odata 请求(如果您使用它)进行身份验证。
这适用于所有浏览器,在客户端记得添加 xhrFiled withCredentials,如下所示。
$.ajax(
type : method,
url : apiUrl,
dataType : "json",
xhrFields:
withCredentials: true
,
async : true,
crossDomain : true,
contentType : "application/json",
data: data ? JSON.stringify(data) : ''
).....
我正在尝试寻找另一种避免使用钩子的解决方案,但直到现在都没有成功, 我会使用 web.config 配置来执行以下操作: 警告下面的配置不起作用!
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
<authorization>
<deny verbs="GET,PUT,POST" users="?" />
<allow verbs="OPTIONS" users="?"/>
</authorization>
</system.web>
<location path="api">
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
【讨论】:
【参考方案8】:我在网上找到的其他解决方案对我不起作用或看起来太老套了;最后我想出了一个更简单且可行的解决方案:
web.config:
<system.web>
...
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
</system.web>
项目属性:
-
开启
Windows Authentication
关闭Anonymous Authentication
设置 CORS:
[assembly: OwinStartup(typeof(Startup))]
namespace MyWebsite
public class Startup
public void Configuration(IAppBuilder app)
app.UseCors(CorsOptions.AllowAll);
这需要 NUget 上可用的 Microsoft.Owin.Cors 程序集。
角度初始化:
$httpProvider.defaults.withCredentials = true;
【讨论】:
【参考方案9】:这是我的解决方案。
Global.asax*
protected void Application_BeginRequest(object sender, EventArgs e)
if(!ListOfAuthorizedOrigins.Contains(Context.Request.Headers["Origin"])) return;
if (Request.HttpMethod == "OPTIONS")
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
HttpContext.Current.Response.StatusCode = 200;
HttpContext.Current.Response.End();
if (Request.Headers.AllKeys.Contains("Origin"))
HttpContext.Current.Response.Headers.Remove("Access-Control-Allow-Origin");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.Headers["Origin"]);
【讨论】:
以上是关于带有 Windows 身份验证的 WebAPI CORS - 允许匿名 OPTIONS 请求的主要内容,如果未能解决你的问题,请参考以下文章
Angular 7 .net 核心 2.2 WebApi Windows 身份验证 CORS
使用 Windows 身份验证在 Web API 控制器中获取 NetworkCredential 或客户端凭据
通过 Angular 调用时,Windows 身份验证不适用于 WebAPI
.Net Core WebAPI CORS 与 Windows 身份验证