所以我有一个使用JWT令牌进行身份验证的.net核心(2.1)API。我可以登录并成功进行身份验证的呼叫。
我正在为客户端使用React(16.6.3),该客户端获取JWT代码并进行经过身份验证的API调用。
我正在尝试将Signaler集线器添加到该站点。如果我没有在集线器类上放置[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令牌。
我无法弄清楚我做错了什么,才能够对Signaler核心进行身份验证的呼叫。
解决方案
我将代码更新为使用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)]
我无法让多个网站和控制台应用程序通过Signalr连接和通信。
答案 0 :(得分:1)
要与[Authorize]
一起使用,您需要设置请求标头。由于Web套接字不支持标头,因此令牌将与您正确解析的查询字符串一起传递。缺少的一件事是
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();
在服务器上,启动。配置:
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();
}