授权在 ASP.NET Core 2.1 的 Signalr 中不起作用

Authorize doesn't work in Signalr of ASP.NET Core 2.1

我已经按照 this tutorial.

将我的项目从 ASP.Net Core 2.0 升级到 ASP.NET Core 2.1

一切都很好,直到我将 Signar Core 2.1 应用到我的项目中。

这是我的Startup.cs

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)
    {
        services.AddSingleton<IAuthorizationHandler, SolidAccountRequirementHandler>();

        services.AddCors(
            options => options.AddPolicy("AllowCors",
                builder =>
                {
                    builder
                        .AllowAnyOrigin()
                        .AllowCredentials()
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                })
        );

        services.AddAuthorization(x =>
        {
            x.AddPolicy("MainPolicy", builder =>
            {
                builder.Requirements.Add(new SolidAccountRequirement());
            });
        });

        services.AddSignalR();

        #region Mvc builder

        var authenticationBuilder = services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme);

        authenticationBuilder.AddJwtBearer(o =>
        {
            // You also need to update /wwwroot/app/scripts/app.js
            o.SecurityTokenValidators.Clear();

            // Initialize token validation parameters.
            var tokenValidationParameters = new TokenValidationParameters();
            tokenValidationParameters.ValidAudience = "audience";
            tokenValidationParameters.ValidIssuer = "issuer";
            tokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SigningKey"));
            tokenValidationParameters.ValidateLifetime = false;

            o.TokenValidationParameters = tokenValidationParameters;
        });

        // Construct mvc options.
        services.AddMvc(mvcOptions =>
            {
                ////only allow authenticated users
                var policy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                    .AddRequirements(new SolidAccountRequirement())
                    .Build();

                mvcOptions.Filters.Add(new AuthorizeFilter(policy));
            })
            .AddJsonOptions(options =>
            {
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;

        #endregion
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        //app.UseHttpsRedirection();
        app.UseCors("AllowCors");

        app.UseSignalR(routes =>
        {
            routes.MapHub<ChatHub>("/chathub");
        });

        app.UseMvc();
    }
}

这是我的SolidRequirementHandler

public class SolidAccountRequirementHandler : AuthorizationHandler<SolidAccountRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SolidAccountRequirement requirement)
    {
        context.Succeed(requirement);
        return Task.CompletedTask;
    }
}

这是我的 ChatHub.cs:

public class ChatHub : Hub
{
    [Authorize(Policy = "MainPolicy")]
    public override Task OnConnectedAsync()
    {
        return base.OnConnectedAsync();
    }
}

当我使用我的 AngularJS 应用程序连接到 ChatHub 时,我期望 MainPolicy 会被调用。但是,OnConnectedAsync() 函数在未检查请求身份的情况下被调用。

MVC 控制器的策略已成功应用,但 Signalr 的策略没有。

有人可以帮我吗?

谢谢,

我将这个问题发布到 Signalr github issue page。 这是他们给我的答案。 我试过了并且成功了:

解决方案是将 [Authorize] 属性放到 ChatHub

[Authorize(Policy = "MainPolicy")]
public class ChatHub : Hub
{
    public override Task OnConnectedAsync()
    {
        return base.OnConnectedAsync();
    }
}

只分享给不知道的人:)

我也有同样的问题,关键有四点:

1- 在您的 Startup.cs 中请注意 Configure(IApplicationBuilder app)

中的此命令
            app.UseRouting();
            app.UseAuthorization( );
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<myChat>("/chat");
            });

app.UseAuthorization( ); 应始终介于 app.UseRouting();app.UseEndpoints() 之间。

2- SignalR 不在 Header 中发送令牌,但它在查询中发送它们。在您的 startup.cs 内部 ConfigureServices(IServiceCollection services) 您必须以某种方式配置您的应用程序以从查询中读取令牌并将它们放入 header。您可以通过这种方式配置您的 JWT:

  services.AddAuthentication()
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false,
                    ValidIssuer = [Issuer Site],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes([YOUR SECRET KEY STRING]))
                };
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var path = context.Request.Path;
                        var accessToken = context.Request.Query["access_token"];
                        if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/chat"))
                        {
                            
                            context.Request.Headers.Add("Authorization", new[] { $"Bearer {accessToken}" });
                        }
                        return Task.CompletedTask;
                    }
                };
            });

3- 您的客户端在想要建立连接时应发送令牌。您可以在建立连接时将令牌添加到查询。

var connection = new signalR.HubConnectionBuilder().withUrl(
"http://localhost:5000/chat", {
    skipNegotiation: true,
    transport: signalR.HttpTransportType.WebSockets,
    accessTokenFactory: () => "My Token Is Here"}).build();

4- 我没有在里面添加默认的 Athuentication 方案 services.AddAuthentication() 所以每次我都必须像这样指定我的授权方案。 [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 最后,您可以像这样 Class 保护您的聊天

using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;

        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public class myChat : Hub
        {
            ///Some functions
        }

看来使用语句很重要,所以一定要使用正确的语句。 SignalR hub Authorize attribute doesn't work

注意:我在 myChat class 中只授权一个方法有问题。不知道为什么。