Междоменные соединения SignalR с самообслуживанием и аутентификацией

У меня проблема с CORS при самообслуживании SignalR с OWIN, что происходит только при попытке включить аутентификацию.

Ошибка, которую я получаю в своем веб-браузере:

XMLHttpRequest не может загрузить http://.../signalr/negotiate?[snip] Происхождение... не разрешено Access-Control-Allow-Origin

Это происходит только в том случае, если я включаю проверку подлинности на своем самообслуживаемом сервере с помощью подхода в этом ответе:

public void Configuration(IAppBuilder app)
{
  var listener = (HttpListener)app.Properties[typeof(HttpListener).FullName];
  listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm; 

  app.MapHubs(new HubConfiguration { EnableCrossDomain = true });
} 

Если я прокомментирую строку AuthenticationSchemes, то работает CORS (и я проверил все в эти инструкции). Я получаю ту же проблему, если я использую другие схемы аутентификации, чем NTLM.

Используя Fiddler, чтобы проверить, что происходит, без проверки подлинности я вижу, что необходимые заголовки CORS возвращаются с сервера:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: [мой сервер]

Однако после включения проверки подлинности я получаю ответ 401, в котором отсутствуют эти заголовки. Все запросы имеют необходимый заголовок Origin.

Изучив исходный код SignalR похоже, что заголовки устанавливаются, но, предположительно, с помощью аутентификации HttpListener отправляет начальный ответ 401 без удара этого кода.

Итак, я думаю, что мой вопрос: как мне включить HttpListener заголовок Access-Control-Allow-Origin в его согласовании протоколов аутентификации?

Ответы

Ответ 1

Я получил аутентификацию NTLM для работы с междоменным сигналом R, самостоятельно размещенным в OWIN, разрешив анонимный доступ к предполетным запросам.

Что нужно сделать, так это создать делегата для выбора схемы проверки подлинности, которая ищет заголовки запросов перед полетом, и позволяет это через анонимность. Все остальные запросы будут использовать NTLM.

public void Configuration(IAppBuilder appBuilder)
{
    var listener = (HttpListener)appBuilder.Properties[typeof(HttpListener).FullName];
    listener.AuthenticationSchemeSelectorDelegate += AuthenticationSchemeSelectorDelegate;
}

private AuthenticationSchemes AuthenticationSchemeSelectorDelegate(HttpListenerRequest httpRequest)
{
    if (httpRequest.Headers.Get("Access-Control-Request-Method")!=null) 
        return AuthenticationSchemes.Anonymous;
    else 
        return AuthenticationSchemes.Ntlm;
}

Ответ 2

Я предполагаю, что вы используете Chrome, который очень бесполезно говорит вам, что эти заголовки отсутствуют, и что это проблема, когда вы, вероятно, просто забыли установить XMLHttpRequest withCredentials до true.

Если вы используете jQuery, вы можете сделать это для всех запросов с помощью:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
  options.xhrFields = { withCredentials: true };
});

Вам также нужно сделать правильные действия с запросами OPTIONS, как в другом ответе.