C# HttpListener несколько схем аутентификации и Chrome

Хорошо, это длинный вопрос, но я думаю, что оно того стоит. Что мы имеем:

  1. Пример фиктивного консольного приложения на 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();
            }
        }
    }
    
  2. Класс запуска 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);
        }
    }
    
  3. Пример контроллера 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 вещи:

  1. /sample/public конечная точка больше не доступна для анонимных запросов (как и ожидалось)
  2. /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;
Другие вопросы по тегам