2013-04-28 8 views
6

Mam własny hostowany interfejs API C# consol. Obsługuje wiele aplikacji internetowych, które wykorzystują AngularJS do wykonywania asynchronicznych żądań HTTP. Musi mieć zarówno obsługę CORS, jak i uwierzytelnianie NTLM.Jak włączyć obsługę CORS i uwierzytelnianie NTLM

Obecnie mam oba włączone, ale wydaje się, że sposób, w jaki je wprowadziłem, powoduje ich anulowanie. Wierzę, że jest to spowodowane przez moją początkową odpowiedź 401 z serwera. Zwykle powoduje to uzgadnianie uwierzytelnienia między dwoma, ale patrząc na to w Fiddler, ta początkowa odpowiedź 401 nie zawiera znacznika Access-Control-Allow-Origin, a to powoduje, że przeglądarka rezygnuje z uzgadniania. Wziąłem aplikację i nadałem im tę samą nazwę hosta, aby wyłączyć potrzebę CORS, a uścisk dłoni działa idealnie. Wyłączyłem uwierzytelnianie NTLM, zastępując moją niestandardową konfigurację NtlmSelfHost z oryginalną konfiguracją HttpSelfHostConfiguration, a tag Access-Control-Allow-Origin działa idealnie, aby umożliwić CORS. tylko wtedy, gdy oba są aktywne, rzeczy nie działają.

AKTUALIZACJA: Umieściłem punkt przerwania w mojej CorsHandle na żądanie, które nie wymagałoby tego, aby upewnić się, że serwer internetowy może go wywołać podczas korzystania z NtlmSelfHostConfiguration, i udało się to. Chciałem tylko upewnić się, że nic nie było w NtlmSelfHostConfiguration, która fizycznie uniemożliwiła zadzwonienie do Corshandle. Wszystko działa dobrze, Zdecydowanie wydaje mi się, że muszę znaleźć sposób, aby zadzwonić do CorsHandle nawet w przypadku próśb, które nie uwierzytelniają się.

Oto moja implimentation:

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

nie mogę wypowiedzieć się na temat aktualnej kwestii, ale aby utworzyć kompletny wynik, możesz po prostu użyć ['Task.FromResult '] (http://msdn.microsoft.com/en-us/library/hh194922.aspx) zamiast ingerować w 'TaskCompletionSource ' – spender

+1

Możesz również użyć 'Access-Control-Allow-Credentials: true' w swoim zasobie CORS. To pozwala na wysyłanie plików cookie. –

+0

Następująca odpowiedź może pomóc: http://stackoverflow.com/a/15734032/107250 – monsur

Odpowiedz

8

To może być trochę późno i Nie jestem pewien, czy to ci pomoże, ale może pomóc komuś, kto chce mieć włączone zarówno NTLM, jak i CORS.

Używam Web API 2 dla interfejsu REST API zaplecza. I AngularJS dla frontendu.

Po pierwsze, ponieważ używasz NTLM, twoje wywołanie XMLHttpRequest musi wysłać referencje. W angularjs zrobić tak:

$http.get(url, { withCredentials: true }); 

Następnie należy włączyć swój CORS na swoim backend (w WebApiConfig.Register()):

 var enableCorsAttribute = new EnableCorsAttribute("*", "*", "*") 
      { 
       SupportsCredentials = true 
      }; 
     config.EnableCors(enableCorsAttribute); 

SupportsCredentials = true oznacza, że ​​dodamy Access-Control-Allow-Credentials do odpowiedzi.

Znaków oznacza, że ​​pozwolimy:

  • CORS z dowolnego pochodzenia
  • Wszystkie nagłówki żądania
  • Wszelkie HTTP czasownik (metoda)
+0

dzięki, że to bardzo mi pomogło ... –