如何同时启用 CORS 支持和 NTLM 身份验证
Posted
技术标签:
【中文标题】如何同时启用 CORS 支持和 NTLM 身份验证【英文标题】:How to enable both CORS support and NTLM authentication 【发布时间】:2013-04-22 13:42:49 【问题描述】:我有一个自托管的 c# consol web API。它为几个使用 AngularJS 执行异步 http 请求的 Web 应用程序提供服务。它需要同时具有 CORS 支持和 NTLM 身份验证。
我目前两者都启用了,但似乎我实现它们的方式会导致它们自行取消。我相信这是由服务器最初的 401 响应造成的。这通常会导致两者之间的身份验证握手,但是,在 Fiddler 中查看它,这个初始的 401 响应缺少 Access-Control-Allow-Origin 标记,这会导致浏览器放弃握手。我已经使用了一个应用程序并为它们提供了相同的主机名以禁用对 CORS 的需求,并且握手效果很好。我通过用原始 HttpSelfHostConfiguration 替换我的自定义 NtlmSelfHostConfiguration 禁用了 NTLM 身份验证,并且 Access-Control-Allow-Origin 标记完美执行以允许 CORS。只有当它们都处于活动状态时,事情才会起作用。
更新:我在我的 CorsHandle 中放置了一个断点,用于一个不需要它的请求,只是为了确保网络服务器可以在使用 NtlmSelfHostConfiguration 时实际调用它,并且它被成功命中。我只是想确保 NtlmSelfHostConfiguration 中没有任何东西在物理上阻止 Corhandle 被调用。一切正常,看来我需要找到一种方法来调用 CorsHandle,即使是在身份验证失败的请求上也是如此。
这是我的暗示:
program.cs:
using System.Web.Http;
using System.Web.Http.Validation.Providers;
using System.Web.Http.SelfHost;
namespace DCMAPI
class Program
static void Main(string[] args)
string BaseAddress = "http://localhost:8080/";
//NtlmSelfHostConfiguration is defined in HttpSelfHostConfiguration.cs
//it inherits from HttpSelfHostConfiguration
//and enables Ntlm Authentication.
var config = new NtlmSelfHostConfiguration(BaseAddress);
config.Routes.MapHttpRoute(
"API Default",
"api/controller/id",
new id = RouteParameter.Optional );
//CorsHandler is also defined in CorsHandler.cs. It is what enables CORS
config.MessageHandlers.Add(new CorsHandler());
var appXmlType =
config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault
(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
config.Services.RemoveAll
(typeof(System.Web.Http.Validation.ModelValidatorProvider),
v => v is InvalidModelValidatorProvider);
using (HttpSelfHostServer server = new HttpSelfHostServer(config))
server.OpenAsync().Wait();
Console.WriteLine("running");
Console.ReadLine();
NtlmSelfHostConfiguration.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.SelfHost;
using System.Web.Http.SelfHost.Channels;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace DCMAPI
public class NtlmSelfHostConfiguration : HttpSelfHostConfiguration
public NtlmSelfHostConfiguration(string baseAddress)
: base(baseAddress)
public NtlmSelfHostConfiguration(Uri baseAddress)
: base(baseAddress)
protected override BindingParameterCollection OnConfigureBinding
(HttpBinding httpBinding)
httpBinding.Security.Mode =
HttpBindingSecurityMode.TransportCredentialOnly;
httpBinding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Ntlm;
return base.OnConfigureBinding(httpBinding);
CorsHandle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
namespace DCMAPI
public class CorsHandler : DelegatingHandler
const string Origin = "Origin";
const string AccessControlRequestMethod = "Access-Control-Request-Method";
const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
protected override Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
bool isCorsRequest = request.Headers.Contains(Origin);
bool isPreflightRequest = request.Method == HttpMethod.Options;
if (isCorsRequest)
if (isPreflightRequest)
HttpResponseMessage response =
new HttpResponseMessage(HttpStatusCode.OK);
response.Headers.Add(AccessControlAllowOrigin,
request.Headers.GetValues(Origin).First());
string accessControlRequestMethod =
request.Headers.GetValues(AccessControlRequestMethod)
.FirstOrDefault();
if (accessControlRequestMethod != null)
response.Headers.Add(
AccessControlAllowMethods, accessControlRequestMethod);
string requestedHeaders = string.Join(", ",
request.Headers.GetValues(AccessControlRequestHeaders));
if (!string.IsNullOrEmpty(requestedHeaders))
response.Headers.Add(AccessControlAllowHeaders,
requestedHeaders);
TaskCompletionSource<HttpResponseMessage> tcs =
new TaskCompletionSource<HttpResponseMessage>();
tcs.SetResult(response);
return tcs.Task;
else
return base.SendAsync(request,
cancellationToken).ContinueWith<HttpResponseMessage>(t =>
HttpResponseMessage resp = t.Result;
resp.Headers.Add(
AccessControlAllowOrigin,
request.Headers.GetValues(Origin).First());
return resp;
);
else
return base.SendAsync(request, cancellationToken);
【问题讨论】:
无法评论您的实际问题,但要创建完整的结果,您可以简单地使用Task.FromResult<T>
而不是乱搞TaskCompletionSource<T>
您可能还希望在您的 CORS 资源中使用 Access-Control-Allow-Credentials: true
。这允许发送 cookie。
以下答案可能会有所帮助:***.com/a/15734032/107250
【参考方案1】:
这可能有点晚了,我不确定它是否会对您有所帮助,但它可能会对希望同时启用 NTLM 和 CORS 的其他人有所帮助。
我将Web API 2
用于后端 REST API。 AngularJS
用于前端。
首先,由于您使用的是 NTLM,因此您的呼叫 XMLHttpRequest
必须发送凭据。在 AngularJs 中你这样做:
$http.get(url, withCredentials: true );
然后,您必须在后端启用 CORS(在 WebApiConfig.Register()
中):
var enableCorsAttribute = new EnableCorsAttribute("*", "*", "*")
SupportsCredentials = true
;
config.EnableCors(enableCorsAttribute);
SupportsCredentials = true
表示我们将在响应中添加Access-Control-Allow-Credentials
。
通配符表示我们允许:
来自任何来源的CORS 所有请求标头 任何 HTTP 动词(方法)【讨论】:
以上是关于如何同时启用 CORS 支持和 NTLM 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
如何支持 NTLM 身份验证并回退到 ASP.NET MVC 中的表单?
具有 Windows 身份验证 NTLM 的 IIS 10 上的 AEM Dispatcher - 启用 Dispatcher 缓存时的身份验证问题
Django 1.11-如何使用仅支持NTLM身份验证的邮件服务器发送邮件