.Net SignalR 在还配置了 Cookie 身份验证时使用 JWT Bearer 身份验证
.Net SignalR use JWT Bearer Authentication when Cookie Authentication is also configured
我有一个 ASP.NET 5 WebApp,它是更大系统的一部分,对浏览器请求使用 Cookie 身份验证。
我想添加请求数据和对某些 Windows 服务执行特定操作的功能,这些服务也是整个系统的一部分,并在几台单独的 PC 上执行。我想为此使用 SignalR。
然后 Windows-服务 运行 作为我们 ActiveDirectory 的一部分的专用服务标识。由于服务不应将其用户凭据存储在代码或本地配置文件中,因此它们正在从与 Windows 身份验证一起使用的 API 请求 Web 应用程序的身份验证令牌。
然后,在与 Web 应用程序建立 SignalR 连接时,服务将使用从 API 收到的令牌对 Web 应用程序进行身份验证。这通常有效。
Web 应用程序的身份验证配置为:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(12);
options.SlidingExpiration = true;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
{
// Configuration details excluded
// ...
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// ...
}
};
根据Microsoft Documentation,这应该是一个有效的身份验证配置。
在 services.AddAuthorization(...)
方法中,我添加了特定于 Bearer 方案的策略:
options.AddPolicy("SignalRService", policy =>
{
policy.RequireRole("SignalRService");
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
});
然后是受此策略保护的 SignalR Hub 方法:
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString) { /*...*/ }
最后 windows 服务中的集线器连接创建如下:
connection = new HubConnectionBuilder()
.WithUrl(hubAddress, options =>
{
options.AccessTokenProvider = () => Task.FromResult(authToken);
})
.WithAutomaticReconnect()
.Build();
正在建立连接:
await connection.StartAsync();
但是当我尝试从 windows 服务调用集线器方法时,例如 await connection.InvokeAsync("RegisterService", clientId);
我收到 HubException
消息:
Failed to invoke 'RegisterService' because user is unauthorized
我还在 Web 应用程序上创建了一个 API 控制器用于测试目的,并使用相同的策略保护它:
[HttpGet]
[Authorize(Policy = "SignalRService")]
public IActionResult Get()
{
return Ok(User.Identity.Name);
}
当我使用我将用于 SignalR Hub 调用的相同令牌调用此 API 端点时,我得到了按预期返回的令牌上的标识集。我还验证了配置的 OnMessageReceived
事件处理程序在这种情况下被执行,而当我使用 SignalR 连接时却没有。
当我将 JwtBearerDefaults.AuthenticationScheme
设置为 Startup.cs
中的默认方案而不是 CookieAuthenticationDefaults.AuthenticationScheme
时,它也适用于 SignalR Hub,但随后我的基于 Cookie 的标准用户身份验证中断。
我希望在调用 Hub 方法时需要一些额外的配置来告诉 Web 应用程序明确使用 Bearer 方案,但到目前为止我找不到任何东西。
再拼命尝试一个小时后,我发现当我将 Authorize(Policy = "SignalRService")
直接放在 class 而不是方法上时,特定的承载身份验证默认使用 Cookie 身份验证.
由于使用 cookie 的浏览器连接也应该可以访问我的集线器,因此我最终得到了:
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
public class SignalRServiceHub : Hub
{
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString)
{
// ...
}
[Authorize(Policy = "Root")]
public async Task RegisterMonitoringClient()
{
// ...
}
我不完全确定为什么在这种情况下需要在 class 级别指定方案,而对于 ApiController
实现则不需要
我有一个 ASP.NET 5 WebApp,它是更大系统的一部分,对浏览器请求使用 Cookie 身份验证。
我想添加请求数据和对某些 Windows 服务执行特定操作的功能,这些服务也是整个系统的一部分,并在几台单独的 PC 上执行。我想为此使用 SignalR。 然后 Windows-服务 运行 作为我们 ActiveDirectory 的一部分的专用服务标识。由于服务不应将其用户凭据存储在代码或本地配置文件中,因此它们正在从与 Windows 身份验证一起使用的 API 请求 Web 应用程序的身份验证令牌。
然后,在与 Web 应用程序建立 SignalR 连接时,服务将使用从 API 收到的令牌对 Web 应用程序进行身份验证。这通常有效。
Web 应用程序的身份验证配置为:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(12);
options.SlidingExpiration = true;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
{
// Configuration details excluded
// ...
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// ...
}
};
根据Microsoft Documentation,这应该是一个有效的身份验证配置。
在 services.AddAuthorization(...)
方法中,我添加了特定于 Bearer 方案的策略:
options.AddPolicy("SignalRService", policy =>
{
policy.RequireRole("SignalRService");
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
});
然后是受此策略保护的 SignalR Hub 方法:
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString) { /*...*/ }
最后 windows 服务中的集线器连接创建如下:
connection = new HubConnectionBuilder()
.WithUrl(hubAddress, options =>
{
options.AccessTokenProvider = () => Task.FromResult(authToken);
})
.WithAutomaticReconnect()
.Build();
正在建立连接:
await connection.StartAsync();
但是当我尝试从 windows 服务调用集线器方法时,例如 await connection.InvokeAsync("RegisterService", clientId);
我收到 HubException
消息:
Failed to invoke 'RegisterService' because user is unauthorized
我还在 Web 应用程序上创建了一个 API 控制器用于测试目的,并使用相同的策略保护它:
[HttpGet]
[Authorize(Policy = "SignalRService")]
public IActionResult Get()
{
return Ok(User.Identity.Name);
}
当我使用我将用于 SignalR Hub 调用的相同令牌调用此 API 端点时,我得到了按预期返回的令牌上的标识集。我还验证了配置的 OnMessageReceived
事件处理程序在这种情况下被执行,而当我使用 SignalR 连接时却没有。
当我将 JwtBearerDefaults.AuthenticationScheme
设置为 Startup.cs
中的默认方案而不是 CookieAuthenticationDefaults.AuthenticationScheme
时,它也适用于 SignalR Hub,但随后我的基于 Cookie 的标准用户身份验证中断。
我希望在调用 Hub 方法时需要一些额外的配置来告诉 Web 应用程序明确使用 Bearer 方案,但到目前为止我找不到任何东西。
再拼命尝试一个小时后,我发现当我将 Authorize(Policy = "SignalRService")
直接放在 class 而不是方法上时,特定的承载身份验证默认使用 Cookie 身份验证.
由于使用 cookie 的浏览器连接也应该可以访问我的集线器,因此我最终得到了:
[Authorize(AuthenticationSchemes = "Bearer,Cookies")]
public class SignalRServiceHub : Hub
{
[Authorize(Policy = "SignalRService")]
public async Task RegisterService(string clientIdString)
{
// ...
}
[Authorize(Policy = "Root")]
public async Task RegisterMonitoringClient()
{
// ...
}
我不完全确定为什么在这种情况下需要在 class 级别指定方案,而对于 ApiController
实现则不需要