【发布时间】:2019-06-20 03:55:03
【问题描述】:
所以我有一个使用 JWT 令牌进行身份验证的 .net 核心 (2.1) API。我可以成功登录并进行经过身份验证的呼叫。
我正在为客户端使用 React (16.6.3),它可以获取 JWT 代码并对 API 进行经过身份验证的调用。
我正在尝试向站点添加信号集线器。如果我没有在集线器类上放置 [Authorize] 属性。我可以连接、发送和接收消息(目前它是一个基本的聊天中心)。
当我将 [Authorize] 属性添加到类时,React 应用程序将向 example.com/hubs/chat/negotiate 发送 HttpPost。我会得到一个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) 这允许网站和信号器 cors 访问的所有连接。
我没有添加
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)]
因此,我无法让多个网站和控制台应用程序通过信号器进行连接和通信。
【问题讨论】:
-
您说令牌确实已设置,因此它可能无法通过验证。您没有包含令牌签名/传输代码。您确定您在服务器端正确签署令牌并在客户端正确存储/刷新它吗?
-
@Eran,我确信令牌是好的,react 应用程序使用存储在本地存储中的 JWT 进行经过身份验证的调用,没有问题。构建集线器时,
accessTokenFactory部分使用相同的标记