gpt4 book ai didi

c# - AspNet.Core,IdentityServer 4 : Unauthorized (401) during websocket handshake with SignalR 1. 0 使用 JWT 不记名 token

转载 作者:太空狗 更新时间:2023-10-29 20:52:39 25 4
gpt4 key购买 nike

我有两个 aspnet.core 服务。一份用于 IdentityServer 4,一份用于 Angular4+ 客户端使用的 API。 SignalR 中心在 API 上运行。整个解决方案在 docker 上运行,但这无关紧要(见下文)。

我使用完美无缺的隐式身份验证流程。 NG 应用程序重定向到用户登录的 IdentityServer 登录页面。之后,浏览器使用访问 token 重定向回 NG 应用程序。然后使用 token 调用 API 并建立与 SignalR 的通信。我想我已经阅读了所有可用的内容(请参阅下面的资源)。

由于 SignalR 使用的是不支持 header 的 websockets,因此应在查询字符串中发送 token 。然后在 API 端提取 token 并为请求设置 token ,就像在 header 中一样。然后验证 token 并授权用户。

API 可以毫无问题地工作,用户获得授权并且可以在 API 端检索声明。所以 IdentityServer 应该没有问题,因为 SignalR 不需要任何特殊配置。我说得对吗?

当我不在 SignalR 集线器上使用 [Authorized] 属性时,握手成功。这就是为什么我认为我使用的 docker 基础设施和反向代理没有任何问题(代理设置为启用 websockets)。

因此,SignalR 无需授权即可工作。通过授权,NG 客户端在握手期间获得以下响应:

Failed to load resource: the server responded with a status of 401
Error: Failed to complete negotiation with the server: Error
Error: Failed to start the connection: Error

请求是

Request URL: https://publicapi.localhost/context/negotiate?signalr_token=eyJhbGciOiJSUz... (token is truncated for simplicity)
Request Method: POST
Status Code: 401
Remote Address: 127.0.0.1:443
Referrer Policy: no-referrer-when-downgrade

我得到的响应:

access-control-allow-credentials: true
access-control-allow-origin: http://localhost:4200
content-length: 0
date: Fri, 01 Jun 2018 09:00:41 GMT
server: nginx/1.13.10
status: 401
vary: Origin
www-authenticate: Bearer

根据日志, token 验证成功。我可以包括完整的日志,但我怀疑问题出在哪里。所以我将在此处包括该部分:

[09:00:41:0561 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: Identity.Application was not authenticated.
[09:00:41:0564 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: Identity.Application was not authenticated.

我在日志文件中得到了这些,但我不确定这是什么意思。我将代码部分包含在 API 中,我在其中获取并提取 token 以及身份验证配置。

services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddIdentityServerAuthentication(options =>
{
options.Authority = "http://identitysrv";
options.RequireHttpsMetadata = false;
options.ApiName = "publicAPI";
options.JwtBearerEvents.OnMessageReceived = context =>
{
if (context.Request.Query.TryGetValue("signalr_token", out StringValues token))
{
context.Options.Authority = "http://identitysrv";
context.Options.Audience = "publicAPI";
context.Token = token;
context.Options.Validate();
}

return Task.CompletedTask;
};
});

系统无其他错误、异常。我可以调试应用程序,一切似乎都很好。

包含的日志行是什么意思?如何调试授权期间发生的事情?

编辑: 我差点忘了说,我认为问题出在身份验证方案上,所以我将每个方案都设置为我认为需要的方案。然而遗憾的是它没有帮助。

我在这里有点无能为力,所以我很感激任何建议。谢谢。

信息来源:

Pass auth token to SignalR

Securing SignalR with IdentityServer

Microsoft docs on SignalR authorization

Another GitHub question

Authenticate against SignalR

Identity.Application was not authenticated

最佳答案

我必须回答我自己的问题,因为我有截止日期,令人惊讶的是我设法解决了这个问题。所以我把它写下来,希望它能对将来的人有所帮助。

首先我需要了解发生了什么,所以我将整个授权机制替换为我自己的。我可以通过这段代码来做到这一点。解决方案不需要它,但是如果有人需要它,这就是解决方法。

services.Configure<AuthenticationOptions>(options =>
{
var scheme = options.Schemes.SingleOrDefault(s => s.Name == JwtBearerDefaults.AuthenticationScheme);
scheme.HandlerType = typeof(CustomAuthenticationHandler);
});

IdentityServerAuthenticationHandler的帮助下并覆盖所有可能的方法我终于明白了 OnMessageRecieved 事件是在检查 token 后执行的。因此,如果在调用 HandleAuthenticateAsync 期间没有任何 token ,则响应将是 401。这有助于我弄清楚将自定义代码放在哪里。

我需要在 token 检索期间实现我自己的“协议(protocol)”。所以我替换了那个机制。

services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme,
options =>
{
options.Authority = "http://identitysrv";
options.TokenRetriever = CustomTokenRetriever.FromHeaderAndQueryString;
options.RequireHttpsMetadata = false;
options.ApiName = "publicAPI";
});

重要的部分是 TokenRetriever 属性以及替换它的内容。

public class CustomTokenRetriever
{
internal const string TokenItemsKey = "idsrv4:tokenvalidation:token";
// custom token key change it to the one you use for sending the access_token to the server
// during websocket handshake
internal const string SignalRTokenKey = "signalr_token";

static Func<HttpRequest, string> AuthHeaderTokenRetriever { get; set; }
static Func<HttpRequest, string> QueryStringTokenRetriever { get; set; }

static CustomTokenRetriever()
{
AuthHeaderTokenRetriever = TokenRetrieval.FromAuthorizationHeader();
QueryStringTokenRetriever = TokenRetrieval.FromQueryString();
}

public static string FromHeaderAndQueryString(HttpRequest request)
{
var token = AuthHeaderTokenRetriever(request);

if (string.IsNullOrEmpty(token))
{
token = QueryStringTokenRetriever(request);
}

if (string.IsNullOrEmpty(token))
{
token = request.HttpContext.Items[TokenItemsKey] as string;
}

if (string.IsNullOrEmpty(token) && request.Query.TryGetValue(SignalRTokenKey, out StringValues extract))
{
token = extract.ToString();
}

return token;
}

这是我的自定义 token 检索器算法,它首先尝试标准 header 和查询字符串以支持常见情况,例如 Web API 调用。但是,如果 token 仍然为空,它会尝试从客户端在 websocket 握手期间放置它的查询字符串中获取它。

编辑:我使用以下客户端 (TypeScript) 代码来为 SignalR 握手提供 token

import { HubConnection, HubConnectionBuilder, HubConnectionState } from '@aspnet/signalr';
// ...
const url = `${apiUrl}/${hubPath}?signalr_token=${accessToken}`;
const hubConnection = new HubConnectionBuilder().withUrl(url).build();
await hubConnection.start();

其中apiUrl、hubPath和accessToken是连接的必要参数。

关于c# - AspNet.Core,IdentityServer 4 : Unauthorized (401) during websocket handshake with SignalR 1. 0 使用 JWT 不记名 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50640316/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com