Подтвердить что ты не робот

Web API/OWIN, SignalR и авторизация

Я разрабатываю прототип приложения AngularJS, Web API, SignalR как потенциальную отправную точку для нового проекта в VS 2013.

На этом этапе я в значительной степени использую законченный код, который визуальная студия генерирует для отдельных учетных записей пользователей.

Там находится строка в коде StartUp.Auth.cs, которая выглядит так.

app.UseOAuthBearerTokens(OAuthOptions);

При этом я могу добавить атрибут [Authorize] для контроллеров, и он отлично работает.

Кстати, с angular мне удалось добавить стандартный заголовок, содержащий токен в JavaScript, следующим образом.

$http.defaults.headers.common.Authorization = 'bearer ' + access_token;

Затем я добавил SignalR в проект.
Он поддерживает собственную версию атрибута [Авторизовать], но при использовании SignalR нельзя передавать пользовательские заголовки.
Это ограничение на стороне браузера.
В документации указано, что вы можете передать токен как часть строки запроса.
Я добавил этот код со стороны JavaScript. Мой код SignalR теперь включил это.
Я передал токен как "bearer_token".

this.connection = $.hubConnection("/TestHub", { useDefaultPath: false, qs: "bearer_token=" + token });

Итак, моя проблема заключалась в том, как сделать OWIN распознать токен, теперь он больше не находится в заголовке.
После нескольких поисков я закончил добавление кода, который перенес маркер из строки запроса в заголовок.
Для моего прототипа я просто добавил небольшой код над исходной строкой в ​​StartUp.Auth.cs.

Итак, теперь это выглядело так:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    Provider = new OAuthBearerAuthenticationProvider()
    {
        OnRequestToken = context =>
        {
            if (context.Request.Path.Value.StartsWith("/TestHub"))
            {
                string bearerToken = context.Request.Query.Get("bearer_token");
                if (bearerToken != null)
                {
                    string[] authorization = new string[] { "bearer " + bearerToken };
                    context.Request.Headers.Add("Authorization", authorization);
                }
            }

            return Task.FromResult(context);
        }
    }
});


// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

Приведенный выше код является грубым, но это прототип, поэтому я просто хотел посмотреть, сработало ли оно, что он сделал.

Наконец, перейдем к вопросу: Является ли это правильным шаблоном для интеграции авторизации маркера на предъявителя с помощью SignalR и конвейера OWIN.
Кажется, я не мог найти много хорошей информации о правильном способе сделать это.

4b9b3361

Ответ 1

Я использую класс следующим образом:

public class OAuthTokenProvider : OAuthBearerAuthenticationProvider
{
    private List<Func<IOwinRequest, string>> _locations;
    private readonly Regex _bearerRegex = new Regex("((B|b)earer\\s)");
    private const string AuthHeader = "Authorization";

    /// <summary>
    /// By Default the Token will be searched for on the "Authorization" header.
    /// <para> pass additional getters that might return a token string</para>
    /// </summary>
    /// <param name="locations"></param>
    public OAuthTokenProvider(params Func<IOwinRequest, string>[] locations)
    {
        _locations = locations.ToList();
        //Header is used by default
        _locations.Add(x => x.Headers.Get(AuthHeader));
    }

    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        var getter = _locations.FirstOrDefault(x => !String.IsNullOrWhiteSpace(x(context.Request)));
        if (getter != null)
        {
            var tokenStr = getter(context.Request);
            context.Token = _bearerRegex.Replace(tokenStr, "").Trim();
        }
        return Task.FromResult<object>(null);
    }
}

Который вместо того, чтобы просто передавать токен заголовку, анализирует его и устанавливает его в контексте.

Затем он может быть использован в конфигурации вашего приложения следующим образом:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
    Provider = new OAuthTokenProvider(
         req => req.Query.Get("bearer_token"),
         req => req.Query.Get("access_token"),
         req => req.Query.Get("token"),
         req => req.Headers.Get("X-Token"))    
});

Тогда следующие стили запросов будут иметь свой токен, не зашифрованный, для использования с аутентификацией и авторизацией:

GET https://www.myapp.com/authorized/endpoint?bearer_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?access_token=123ABC HTTP/1.1
GET https://www.myapp.com/authorized/endpoint?token=123ABC HTTP/1.1

GET https://www.myapp.com/authorized/endpoint HTTP/1.1
X-Token: 123ABC

GET https://www.myapp.com/authorized/endpoint HTTP/1.1
Authorization: 123ABC

Ответ 2

Это как я решил это

var authData = localStorageService.get('authorizationData');
var token = authData.token;
 $.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + token };

Никаких изменений на стороне сервера

Надеюсь, что это поможет кому-то

Ответ 3

Я отправлю это для людей, которые будут иметь эту проблему в будущем:

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

  • Первое, что я видел, делает SignalR работу с заголовками, что кажется очень простым в реализации:

    $.signalR.ajaxDefaults.headers = { Authorization: "Bearer " + token };

Огромный недостаток этого заключается в том, что он заставляет SignalR использовать longPolling, который вам определенно не нужен, поскольку вы используете SignalR.

  1. Второй подход - передать access token, который клиент получает при входе в систему как query string, перед подключением. Затем создайте пользовательский Attribute, который наследует AuthorizeAttribute (следовать этому репо - не очень, на мой взгляд, но это делает точку).

Другой подход с передачей токена как query string заключается в следовать этому ответу SO, который создает OAuth Provider и

извлекает все остальные значения из токена в начале конвейера

Опять же, этот метод не является оптимальным, поскольку query strings довольно уязвим:

Строки запроса

как правило, хранятся в журналах веб-сервера (или отображаются в другими способами, даже при использовании SSL). Существует риск кого-то перехватывая токены. Вам нужно выбрать подход, который лучше всего.

  1. Решение, которое я в настоящее время пытаюсь реализовать (и будет возвращаться с обновлениями после его работы: D) на основе этого сообщения в блоге который использует OAuth Bearer Token authentication с SignalR, передавая токен над файлом cookie в конвейер SignalR.

Я считаю, что это решение с наименьшими компромиссами, но я вернусь с дополнительной информацией после завершения моей реализации.

Надеюсь, это поможет. Удачи!

UPDATE Мне удалось получить аутентификацию токена WebApi для работы с SignalR, сохранив токен в файле cookie, а затем извлеките его в провайдер.

Вы cah возьмите посмотрите на это репо GitHub, но я в основном следил за статьей сверху. Явно, вот что я сделал:

Я создал класс OAuthBearerTokenAuthenticationProvider, который наследует от OAuthBearerAuthenticationProvider и переопределяет метод RequestToken.

Теперь он ищет файл cookie, в котором хранится BearerToken и получает его значение. Затем он устанавливает значение context.Token в значение cookie.

Затем на части JavaScript вам нужно получить токен (путем аутентификации с использованием имени пользователя и пароля) и сохранить токен в файле cookie.

public class OAuthBearerTokenAuthenticationProvider : OAuthBearerAuthenticationProvider
{
    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        var tokenCookie = context.OwinContext.Request.Cookies["BearerToken"];

        if (!String.IsNullOrEmpty(tokenCookie))
            context.Token = tokenCookie;

        return Task.FromResult<object>(null);
    }
}

Для рабочего образца, пожалуйста, взгляните на репо выше.

Удачи!