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);
}
}
}
}
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
Możesz również użyć 'Access-Control-Allow-Credentials: true' w swoim zasobie CORS. To pozwala na wysyłanie plików cookie. –
Następująca odpowiedź może pomóc: http://stackoverflow.com/a/15734032/107250 – monsur