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.
Кажется, я не мог найти много хорошей информации о правильном способе сделать это.
Ответы
Ответ 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.
- Второй подход - передать
access token
, который клиент получает при входе в систему как query string
, перед подключением. Затем создайте пользовательский Attribute
, который наследует AuthorizeAttribute
(следовать этому репо - не очень, на мой взгляд, но это делает точку).
Другой подход с передачей токена как query string
заключается в следовать этому ответу SO, который создает OAuth Provider
и
извлекает все остальные значения из токена в начале конвейера
Опять же, этот метод не является оптимальным, поскольку query strings
довольно уязвим:
Строки запроса как правило, хранятся в журналах веб-сервера (или отображаются в другими способами, даже при использовании SSL). Существует риск кого-то перехватывая токены. Вам нужно выбрать подход, который лучше всего.
- Решение, которое я в настоящее время пытаюсь реализовать (и будет возвращаться с обновлениями после его работы: 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);
}
}
Для рабочего образца, пожалуйста, взгляните на репо выше.
Удачи!