ядро сигнализатора (2.1) Центр аутентификации JWT / согласование 401 Несанкционированный - PullRequest
0 голосов
/ 26 января 2019

, поэтому у меня есть .net core (2.1) API, который использует токены JWT для аутентификации. Я могу войти в систему и успешно выполнять аутентифицированные звонки.

Я использую React (16.6.3) для клиента, который работает с кодом JWT и выполняет аутентифицированные вызовы API.

Я пытаюсь добавить концентраторы сигналов на сайт. Если я не помещу атрибут [Authorize] в класс концентратора. Я могу подключаться, отправлять и получать сообщения (на данный момент это базовый чатуб).

когда я добавляю атрибут [Authorize] в класс, приложение React сделает HttpPost равным example.com/hubs/chat/negotiate. Я бы получил 401 код состояния. заголовок Authorization: Bearer abc..... будет пропущен.

Чтобы построить концентратор в React, я использую:

const hubConn = new signalR.HubConnectionBuilder()
            .withUrl(`${baseUrl}/hubs/chat`, { accessTokenFactory: () => jwt })
            .configureLogging(signalR.LogLevel.Information)
            .build();

где переменная jwt является токеном.

У меня есть некоторые настройки для аутентификации:

services.AddAuthentication(a =>
{
    a.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    a.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.SaveToken = false;
    options.Audience = jwtAudience;

options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = jwtIssuer,
    ValidAudience = jwtAudience,
    RequireExpirationTime = true,
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtKey)),

};

// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or 
// Server-Sent Events request comes in.
options.Events = new JwtBearerEvents
{
    OnMessageReceived = context =>
    {
        var accessToken = context.Request.Query["access_token"];
        var authToken = context.Request.Headers["Authorization"].ToString();

        var token = !string.IsNullOrEmpty(accessToken) ? accessToken.ToString() : !string.IsNullOrEmpty(authToken) ?  authToken.Substring(7) : String.Empty;

        var path = context.HttpContext.Request.Path;

        // If the request is for our hub...
        if (!string.IsNullOrEmpty(token) && path.StartsWithSegments("/hubs"))
        {
            // Read the token out of the query string
            context.Token = token;
        }
        return Task.CompletedTask;
    }                    
};

});

событие OnMessageReceived получает удар, а context.Token получает значение токена JWT.

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


решение

Я обновил свой код для использования 2.2 (не уверен, действительно ли это требовалось).

, поэтому я потратил некоторое время на просмотр исходного кода и примеров внутри:

https://github.com/aspnet/AspNetCore

У меня была проблема с Signalr CORS, которая была решена с помощью:

services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials()
                    .SetIsOriginAllowed((host) => true) //allow all connections (including Signalr)
                );
        });

важной частью является .SetIsOriginAllowed((host) => true) Это позволяет всем соединениям как для веб-сайта, так и для доступа к сигналам.

Я не добавил

services.AddAuthorization(options =>
    {
        options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
        {
            policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
            policy.RequireClaim(ClaimTypes.NameIdentifier);
        });
    });

Я только использовал services.AddAuthentication(a =>

Я взял следующее непосредственно из образцов в github

            options.Events = new JwtBearerEvents
            {
                OnMessageReceived = context =>
                {
                    var accessToken = context.Request.Query["access_token"];

                    if (!string.IsNullOrEmpty(accessToken) &&
                        (context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
                    {
                        context.Token = context.Request.Query["access_token"];
                    }
                    return Task.CompletedTask;
                }
            };   

Не уверен, нужно ли это в атрибуте, но то же самое использовал его в своих хабах

    [Authorize(JwtBearerDefaults.AuthenticationScheme)]

из-за этого я не смог подключить несколько веб-сайтов и консольных приложений и общаться через сигнализатор.

1 Ответ

0 голосов
/ 05 февраля 2019

Для использования с [Authorize] необходимо установить заголовок запроса. Поскольку веб-сокеты не поддерживают заголовки, токен передается со строкой запроса, которую вы правильно анализируете. Единственное, чего не хватает, это

context.Request.Headers.Add("Authorization", "Bearer " + token);

Или, в вашем случае, вероятно context.HttpContext.Request.Headers.Add("Authorization", "Bearer " + token);

Пример:

Вот как я это делаю. На клиенте:

const signalR = new HubConnectionBuilder().withUrl(`${this.hubUrl}?token=${token}`).build();

На сервере, в Startup.Configure:

app.Use(async (context, next) => await AuthQueryStringToHeader(context, next));
// ...
app.UseSignalR(r => r.MapHub<SignalRHub>("/hubUrl"));

Реализация AuthQueryStringToHeader:

private async Task AuthQueryStringToHeader(HttpContext context, Func<Task> next)
{
    var qs = context.Request.QueryString;

    if (string.IsNullOrWhiteSpace(context.Request.Headers["Authorization"]) && qs.HasValue)
    {
        var token = (from pair in qs.Value.TrimStart('?').Split('&')
                     where pair.StartsWith("token=")
                     select pair.Substring(6)).FirstOrDefault();

        if (!string.IsNullOrWhiteSpace(token))
        {
            context.Request.Headers.Add("Authorization", "Bearer " + token);
        }
    }

    await next?.Invoke();
}
...