如何同时启用CORS支持和NTLM身份验证

本文关键字:NTLM 身份验证 支持 CORS 何同时 启用 | 更新日期: 2023-09-27 18:11:11

我有一个自托管的c#控制台web API。它服务于几个使用AngularJS执行异步http请求的web应用程序。它需要同时具有CORS支持和NTLM身份验证。

我目前都启用了,但似乎我实现它们的方式会导致它们自己取消。我相信这是我从服务器发出的初始401响应的情况。这通常会导致两者之间的身份验证握手,但是,在Fiddler中查看它,这个初始401响应缺少Access-Control-Allow-Origin标记,这导致浏览器放弃握手。我使用了一个应用程序,并为它们赋予了相同的主机名,以禁用对CORS的需求,握手工作得很好。我通过用原始的HttpSelfHostConfiguration替换我的自定义NtlmSelfHostConfiguration来禁用NTLM身份验证,并且Access-Control-Allow-Origin标记完美地执行以允许CORS。只有当两者都活跃时,事情才不会起作用。

更新:我在CorsHandle中放置了一个断点,该断点不需要它,只是为了确保web服务器在使用NtlmSelfHostConfiguration时可以调用它,并且它成功命中。我只是想确保在NtlmSelfHostConfiguration中没有任何物理上阻止Corshandle被调用。这一切都很好,显然,我需要找到一种方法来调用corhandle,甚至在请求失败的身份验证。

下面是我的实现:

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);
            }
        }
    }
}

如何同时启用CORS支持和NTLM身份验证

这可能有点晚了,我不确定它是否会对您有帮助,但它可能会帮助那些希望同时启用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动词(方法)