C# HttpListener несколько схем аутентификации и Chrome
Хорошо, это длинный вопрос, но я думаю, что оно того стоит. Что мы имеем:
Пример фиктивного консольного приложения на C#, которое запускает собственный хост в службе ASP.Net WebAPI (
Microsoft.AspNet.WebApi.OwinSelfHost
Пакет NuGet):class Program { static void Main(string[] args) { var url = "http://localhost:8080/"; Console.WriteLine($"Starting on {url}"); using (WebApp.Start<Startup>(url)) { Console.WriteLine("Success! Press any key to stop..."); Console.ReadKey(); } } }
Класс запуска OWIN:
class Startup { public void Configuration(IAppBuilder app) { // enable Windows AND Anonymous authentication var listener = app.Properties["System.Net.HttpListener"] as HttpListener; listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous | AuthenticationSchemes.IntegratedWindowsAuthentication; // configure WebAPI var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); app.UseWebApi(config); } }
Пример контроллера WebAPI с двумя открытыми методами:
[RoutePrefix("sample"), Authorize] public class SampleController : ApiController { [Route("public"), AllowAnonymous] public object GetPublicSample() { var message = $"Hi there, mr. {User?.Identity?.Name ?? "ANONYMOUS"}"; return new { sample = 0, message }; } [Route("protected")] public object GetProtectedSample() { var message = $"Hi there, mr. {User?.Identity?.Name ?? "ANONYMOUS"}"; return new { sample = 42, message }; } }
Теперь, когда мы запускаем наш проект и указываем chrome на http://localhost:8080/sample/public, этот запрос вызывается:
GET /sample/public HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
Content-Length: 50
Content-Type: application/json; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Wed, 28 Feb 2018 08:05:56 GMT
{"sample":0,"message":"Hi there, mr. ANONYMOUS"}
Но когда мы заходим на http://localhost:8080/sample/protected, мы имеем это:
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 401 Unauthorized
date: Wed, 28 Feb 2018 08:19:01 GMT
www-authenticate: Negotiate,NTLM
server: Microsoft-HTTPAPI/2.0
content-length: 61
content-type: application/json; charset=utf-8
{"Message":"Authorization has been denied for this request."}
И это почти "как ожидалось", за исключением 1 вещи. Я ожидаю, что когда мой браузер получит 401 HTTP-ответ с www-authenticate
заголовок (и), он попытается повторить тот же запрос с указанной аутентификацией (если там, где нет другого заголовка авторизации в запросе). Но он почему-то нет:(
Чтобы сделать вещи более интересными, мы можем видеть, что AuthenticationSchemes.IntegratedWindowsAuthentication
действительно AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm
и когда я удаляю одну из них, все начинает работать как положено! Например: заменить IntegratedWindowsAuthentication
с Negotiate
в нашем классе запуска и отправьте ваш браузер по http://localhost:8080/sample/protected
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
date: Wed, 28 Feb 2018 08:29:55 GMT
www-authenticate: tOkeN1231351234153=
server: Microsoft-HTTPAPI/2.0
content-length: 59
content-type: application/json; charset=utf-8
{"sample":42,"message":"Hi there, mr. DOMAIN\\username"}
Как правило, наш сервер первый ответ с 401 HTTP-статус и установить заголовок www-authenticate: Negotiate
а затем браузер повторить запрос с дополнительным заголовком авторизации. То же самое, если мы заменим IntegratedWindowsAuthentication
с Ntlm
,
И еще один пример, чтобы прояснить ситуацию. Если мы удалим AuthenticationSchemes.Anonymous
и оставить только AuthenticationSchemes.IntegratedWindowsAuthentication
в результате мы заметим 2 вещи:
/sample/public
конечная точка больше не доступна для анонимных запросов (как и ожидалось)/sample/protected
конечная точка теперь работает как надо (!)
И если мы посмотрим на первый ответ сервера 401, мы заметим, что вместо одного (как прежде) есть два заголовка www-authenticate:
GET /sample/protected HTTP/1.1
Host: localhost:8080
HTTP/1.1 401 Unauthorized
Content-Length: 0
Server: Microsoft-HTTPAPI/2.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
Date: Wed, 28 Feb 2018 08:44:04 GMT
Итак, мои вопросы: "Хорошо", чтобы поместить несколько схем аутентификации в одну www-authenticate
заголовок? Если "Да, все в порядке", почему мой хром не справляется с этой ситуацией? Если "Нет! Это все неправильно!", То почему HttpListener
действовать так, и как я могу обойти это? Пожалуйста, HLP!
1 ответ
Это было задано некоторое время назад, но я хотел бы предоставить свой.02. AuthenticationSchemes.IntegratedWindowsAuthentication немного странно. IWA - это то же самое, что настройка Negotiate и NTLM. Переговоры, с другой стороны, вернутся к NTLM в случае сбоя Kerberos. Ядро ASP.NET не содержит IWA в перечислении: См. Перечисление ASP.NET Core AuthenticationSchemes
Я использую:
listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous | AuthenticationSchemes.Negotiate;