ASP.Net 核心 SignalR 身份验证始终响应 403 - 禁止访问
ASP.Net Core SignalR authentication always responding with 403 - Forbidden
总结
我正在尝试将 security/authentication 添加到我的 SignalR 集线器,但无论我尝试什么,客户端请求都会不断收到 403 - 禁止响应(尽管请求已成功通过身份验证)。
设置
我的项目基于 Microsoft 的 SignalRChat 示例,来自:
基本上我有一个带有 Razor Pages 的 ASP.Net 核心网络应用程序。该项目的目标是.Net Core 3.1。
正在使用的客户端库是 Microsoft JavaScript 客户端库的 v3.1.0。
安全方面我也参考了他们的认证授权文档:
https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1
主要区别在于我没有使用 JWT Bearer 中间件,而是制作了自己的自定义令牌身份验证处理程序。
代码
chat.js:
"use strict";
var connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub", { accessTokenFactory: () => 'mytoken' })
.configureLogging(signalR.LogLevel.Debug)
.build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
using SignalRChat.Security;
namespace SignalRChat
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Found other cases where CORS caused authentication issues, so
// making sure that everything is allowed.
services.AddCors(options =>
{
options.AddPolicy("AllowAny", policy =>
{
policy
.WithOrigins("http://localhost:44312/", "https://localhost:44312/")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services
.AddAuthentication()
.AddHubTokenAuthenticationScheme();
services.AddAuthorization(options =>
{
options.AddHubAuthorizationPolicy();
});
services.AddRazorPages();
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
}
}
ChatHub.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using SignalRChat.Security;
using System.Threading.Tasks;
namespace SignalRChat.Hubs
{
[Authorize(HubRequirementDefaults.PolicyName)]
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
HubTokenAuthenticationHandler.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubTokenAuthenticationHandler : AuthenticationHandler<HubTokenAuthenticationOptions>
{
public IServiceProvider ServiceProvider { get; set; }
public HubTokenAuthenticationHandler(
IOptionsMonitor<HubTokenAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IServiceProvider serviceProvider)
: base(options, logger, encoder, clock)
{
ServiceProvider = serviceProvider;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
bool isValid = TryAuthenticate(out AuthenticationTicket ticket, out string message);
if (isValid) return Task.FromResult(AuthenticateResult.Success(ticket));
return Task.FromResult(AuthenticateResult.Fail(message));
}
private bool TryAuthenticate(out AuthenticationTicket ticket, out string message)
{
message = null;
ticket = null;
var token = GetToken();
if (string.IsNullOrEmpty(token))
{
message = "Token is missing";
return false;
}
bool tokenIsValid = token.Equals("mytoken");
if (!tokenIsValid)
{
message = $"Token is invalid: token={token}";
return false;
}
var claims = new[] { new Claim("token", token) };
var identity = new ClaimsIdentity(claims, nameof(HubTokenAuthenticationHandler));
ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), Scheme.Name);
return true;
}
#region Get Token
private string GetToken()
{
string token = Request.Query["access_token"];
if (string.IsNullOrEmpty(token))
{
token = GetTokenFromHeader();
}
return token;
}
private string GetTokenFromHeader()
{
string token = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(token)) return null;
// The Authorization header value should be in the format "Bearer [token_value]"
string[] authorizationParts = token.Split(new char[] { ' ' });
if (authorizationParts == null || authorizationParts.Length < 2) return token;
return authorizationParts[1];
}
#endregion
}
}
HubTokenAuthenticationOptions.cs
using Microsoft.AspNetCore.Authentication;
namespace SignalRChat.Security
{
public class HubTokenAuthenticationOptions : AuthenticationSchemeOptions { }
}
HubTokenAuthenticationDefaults.cs
using Microsoft.AspNetCore.Authentication;
using System;
namespace SignalRChat.Security
{
public static class HubTokenAuthenticationDefaults
{
public const string AuthenticationScheme = "HubTokenAuthentication";
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder)
{
return AddHubTokenAuthenticationScheme(builder, (options) => { });
}
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder, Action<HubTokenAuthenticationOptions> configureOptions)
{
return builder.AddScheme<HubTokenAuthenticationOptions, HubTokenAuthenticationHandler>(AuthenticationScheme, configureOptions);
}
}
}
HubRequirement.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubRequirement : AuthorizationHandler<HubRequirement, HubInvocationContext>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HubRequirement requirement, HubInvocationContext resource)
{
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
HubRequirementDefaults.cs
using Microsoft.AspNetCore.Authentication;
using System;
namespace SignalRChat.Security
{
public static class HubTokenAuthenticationDefaults
{
public const string AuthenticationScheme = "HubTokenAuthentication";
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder)
{
return AddHubTokenAuthenticationScheme(builder, (options) => { });
}
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder, Action<HubTokenAuthenticationOptions> configureOptions)
{
return builder.AddScheme<HubTokenAuthenticationOptions, HubTokenAuthenticationHandler>(AuthenticationScheme, configureOptions);
}
}
}
结果
在客户端,我在浏览器的开发者控制台中看到以下错误:
- POST https://localhost:44312/chatHub/negotiate?negotiateVersion=1 403
- 错误:无法完成与服务器的协商:错误
- 错误:无法启动连接:错误
在服务器端,我看到的是:
- SignalRChat.Security.HubTokenAuthenticationHandler:调试:AuthenticationScheme:HubTokenAuthentication 已成功通过身份验证。
- SignalRChat.Security.HubTokenAuthenticationHandler: 信息:
AuthenticationScheme:HubTokenAuthentication 被禁止。
后续步骤
我确实看到其他人遇到了 CORS 问题,导致他们无法安全工作,但我相信它通常会在客户端错误中明确指出。尽管如此,我还是在 Startup.cs 中添加了 CORS 策略,我认为 应该 已经规避了这一点。
我也尝试过在 Startup 中更改服务配置的顺序,但似乎没有任何帮助。
如果我删除授权属性(即有一个未经身份验证的集线器)一切正常。
最后发现服务器端的消息很有意思,认证成功了,但是请求还是被禁止了。
我不太确定从这里到哪里去。任何见解将不胜感激。
更新
我已经能够对此进行一些调试。
通过加载系统符号并向上移动调用堆栈,我找到了 Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator:
可以看到,认证成功了,显然授权没有成功。查看要求,有两个:DenyAnonymousAuthorizationRequirement 和我的 HubRequirement(自动成功)。
因为调试器从未在我的 HubRequirement class 中遇到我的断点,所以我只能假设 DenyAnonymousAuthorizationRequirement 是失败的原因。有趣,因为根据 github (https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authorization/Core/src/DenyAnonymousAuthorizationRequirement.cs) 上的代码清单,我应该满足所有要求:
在上下文中定义了一个用户,该用户有一个身份,没有未经身份验证的身份。
我一定是遗漏了一些东西,因为这并没有加起来。
事实证明失败实际上发生在我的 HubRequirement class 中,而不是 DenyAnonymousAuthorizationRequirement。
虽然我的 HubRequirement class 实现了 HandleRequirementAsync(),但它没有实现 HandleAsync(),这恰好是所调用的。
如果我将 HubRequirement class 更新为以下内容,一切都会按预期进行:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubRequirement : AuthorizationHandler<HubRequirement, HubInvocationContext>, IAuthorizationRequirement
{
public override Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var requirement in context.PendingRequirements)
{
// TODO: Validate each requirement
}
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(this);
return Task.CompletedTask;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HubRequirement requirement, HubInvocationContext resource)
{
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
谢谢,为我节省了很多调试时间!
看起来问题是 HandleAsync 也被调用了信号根和协商 url 的 RouteEndpoint 资源,基础 class 不处理这种情况,并且由于没有授权处理程序发出成功信号,因此它失败了。
public override async Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.Resource is HubInvocationContext)
{
foreach (var req in context.Requirements.OfType<RealtimeHubSecurityAuthorizationHandler>())
{
await HandleRequirementAsync(context, req, (HubInvocationContext)context.Resource);
}
} else if (context.Resource is Microsoft.AspNetCore.Routing.RouteEndpoint) {
//allow signalr root and negotiation url
context.Succeed(this);
}
}
(由于评论长度有限,因此作为答案发布,抱歉)
总结
我正在尝试将 security/authentication 添加到我的 SignalR 集线器,但无论我尝试什么,客户端请求都会不断收到 403 - 禁止响应(尽管请求已成功通过身份验证)。
设置
我的项目基于 Microsoft 的 SignalRChat 示例,来自:
基本上我有一个带有 Razor Pages 的 ASP.Net 核心网络应用程序。该项目的目标是.Net Core 3.1。
正在使用的客户端库是 Microsoft JavaScript 客户端库的 v3.1.0。
安全方面我也参考了他们的认证授权文档:
https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1
主要区别在于我没有使用 JWT Bearer 中间件,而是制作了自己的自定义令牌身份验证处理程序。
代码
chat.js:
"use strict";
var connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub", { accessTokenFactory: () => 'mytoken' })
.configureLogging(signalR.LogLevel.Debug)
.build();
//Disable send button until connection is established
document.getElementById("sendButton").disabled = true;
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
connection.start().then(function () {
document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("sendButton").addEventListener("click", function (event) {
var user = document.getElementById("userInput").value;
var message = document.getElementById("messageInput").value;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SignalRChat.Hubs;
using SignalRChat.Security;
namespace SignalRChat
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Found other cases where CORS caused authentication issues, so
// making sure that everything is allowed.
services.AddCors(options =>
{
options.AddPolicy("AllowAny", policy =>
{
policy
.WithOrigins("http://localhost:44312/", "https://localhost:44312/")
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services
.AddAuthentication()
.AddHubTokenAuthenticationScheme();
services.AddAuthorization(options =>
{
options.AddHubAuthorizationPolicy();
});
services.AddRazorPages();
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
}
}
ChatHub.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using SignalRChat.Security;
using System.Threading.Tasks;
namespace SignalRChat.Hubs
{
[Authorize(HubRequirementDefaults.PolicyName)]
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
HubTokenAuthenticationHandler.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubTokenAuthenticationHandler : AuthenticationHandler<HubTokenAuthenticationOptions>
{
public IServiceProvider ServiceProvider { get; set; }
public HubTokenAuthenticationHandler(
IOptionsMonitor<HubTokenAuthenticationOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IServiceProvider serviceProvider)
: base(options, logger, encoder, clock)
{
ServiceProvider = serviceProvider;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
bool isValid = TryAuthenticate(out AuthenticationTicket ticket, out string message);
if (isValid) return Task.FromResult(AuthenticateResult.Success(ticket));
return Task.FromResult(AuthenticateResult.Fail(message));
}
private bool TryAuthenticate(out AuthenticationTicket ticket, out string message)
{
message = null;
ticket = null;
var token = GetToken();
if (string.IsNullOrEmpty(token))
{
message = "Token is missing";
return false;
}
bool tokenIsValid = token.Equals("mytoken");
if (!tokenIsValid)
{
message = $"Token is invalid: token={token}";
return false;
}
var claims = new[] { new Claim("token", token) };
var identity = new ClaimsIdentity(claims, nameof(HubTokenAuthenticationHandler));
ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), Scheme.Name);
return true;
}
#region Get Token
private string GetToken()
{
string token = Request.Query["access_token"];
if (string.IsNullOrEmpty(token))
{
token = GetTokenFromHeader();
}
return token;
}
private string GetTokenFromHeader()
{
string token = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(token)) return null;
// The Authorization header value should be in the format "Bearer [token_value]"
string[] authorizationParts = token.Split(new char[] { ' ' });
if (authorizationParts == null || authorizationParts.Length < 2) return token;
return authorizationParts[1];
}
#endregion
}
}
HubTokenAuthenticationOptions.cs
using Microsoft.AspNetCore.Authentication;
namespace SignalRChat.Security
{
public class HubTokenAuthenticationOptions : AuthenticationSchemeOptions { }
}
HubTokenAuthenticationDefaults.cs
using Microsoft.AspNetCore.Authentication;
using System;
namespace SignalRChat.Security
{
public static class HubTokenAuthenticationDefaults
{
public const string AuthenticationScheme = "HubTokenAuthentication";
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder)
{
return AddHubTokenAuthenticationScheme(builder, (options) => { });
}
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder, Action<HubTokenAuthenticationOptions> configureOptions)
{
return builder.AddScheme<HubTokenAuthenticationOptions, HubTokenAuthenticationHandler>(AuthenticationScheme, configureOptions);
}
}
}
HubRequirement.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubRequirement : AuthorizationHandler<HubRequirement, HubInvocationContext>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HubRequirement requirement, HubInvocationContext resource)
{
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
HubRequirementDefaults.cs
using Microsoft.AspNetCore.Authentication;
using System;
namespace SignalRChat.Security
{
public static class HubTokenAuthenticationDefaults
{
public const string AuthenticationScheme = "HubTokenAuthentication";
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder)
{
return AddHubTokenAuthenticationScheme(builder, (options) => { });
}
public static AuthenticationBuilder AddHubTokenAuthenticationScheme(this AuthenticationBuilder builder, Action<HubTokenAuthenticationOptions> configureOptions)
{
return builder.AddScheme<HubTokenAuthenticationOptions, HubTokenAuthenticationHandler>(AuthenticationScheme, configureOptions);
}
}
}
结果
在客户端,我在浏览器的开发者控制台中看到以下错误:
- POST https://localhost:44312/chatHub/negotiate?negotiateVersion=1 403
- 错误:无法完成与服务器的协商:错误
- 错误:无法启动连接:错误
在服务器端,我看到的是:
- SignalRChat.Security.HubTokenAuthenticationHandler:调试:AuthenticationScheme:HubTokenAuthentication 已成功通过身份验证。
- SignalRChat.Security.HubTokenAuthenticationHandler: 信息: AuthenticationScheme:HubTokenAuthentication 被禁止。
后续步骤
我确实看到其他人遇到了 CORS 问题,导致他们无法安全工作,但我相信它通常会在客户端错误中明确指出。尽管如此,我还是在 Startup.cs 中添加了 CORS 策略,我认为 应该 已经规避了这一点。
我也尝试过在 Startup 中更改服务配置的顺序,但似乎没有任何帮助。
如果我删除授权属性(即有一个未经身份验证的集线器)一切正常。
最后发现服务器端的消息很有意思,认证成功了,但是请求还是被禁止了。
我不太确定从这里到哪里去。任何见解将不胜感激。
更新
我已经能够对此进行一些调试。
通过加载系统符号并向上移动调用堆栈,我找到了 Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator:
可以看到,认证成功了,显然授权没有成功。查看要求,有两个:DenyAnonymousAuthorizationRequirement 和我的 HubRequirement(自动成功)。
因为调试器从未在我的 HubRequirement class 中遇到我的断点,所以我只能假设 DenyAnonymousAuthorizationRequirement 是失败的原因。有趣,因为根据 github (https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authorization/Core/src/DenyAnonymousAuthorizationRequirement.cs) 上的代码清单,我应该满足所有要求:
在上下文中定义了一个用户,该用户有一个身份,没有未经身份验证的身份。
我一定是遗漏了一些东西,因为这并没有加起来。
事实证明失败实际上发生在我的 HubRequirement class 中,而不是 DenyAnonymousAuthorizationRequirement。
虽然我的 HubRequirement class 实现了 HandleRequirementAsync(),但它没有实现 HandleAsync(),这恰好是所调用的。
如果我将 HubRequirement class 更新为以下内容,一切都会按预期进行:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRChat.Security
{
public class HubRequirement : AuthorizationHandler<HubRequirement, HubInvocationContext>, IAuthorizationRequirement
{
public override Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var requirement in context.PendingRequirements)
{
// TODO: Validate each requirement
}
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(this);
return Task.CompletedTask;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HubRequirement requirement, HubInvocationContext resource)
{
// Authorization logic goes here. Just calling it a success for demo purposes.
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
谢谢,为我节省了很多调试时间!
看起来问题是 HandleAsync 也被调用了信号根和协商 url 的 RouteEndpoint 资源,基础 class 不处理这种情况,并且由于没有授权处理程序发出成功信号,因此它失败了。
public override async Task HandleAsync(AuthorizationHandlerContext context)
{
if (context.Resource is HubInvocationContext)
{
foreach (var req in context.Requirements.OfType<RealtimeHubSecurityAuthorizationHandler>())
{
await HandleRequirementAsync(context, req, (HubInvocationContext)context.Resource);
}
} else if (context.Resource is Microsoft.AspNetCore.Routing.RouteEndpoint) {
//allow signalr root and negotiation url
context.Succeed(this);
}
}
(由于评论长度有限,因此作为答案发布,抱歉)