Adding Authorization to one of my SignalR Hubs leads to WebSocket failing to connect

huangapple go评论59阅读模式
英文:

Adding Authorization to one of my SignalR Hubs leads to WebSocket failing to connect

问题

我正在使用SignalR Hub来处理在线国际象棋应用程序的匹配,我正在尝试使用JWT授权限制Hub的使用

这是Hub背后的代码

```csharp
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class GameHub : Hub
{
    private readonly ApplicationDbContext context;
    public static List<Player> _Players = new List<Player>();

    public GameHub(ApplicationDbContext context)
    {
        this.context = context;
    }

    public async Task SendMessage(string user, string message)
    {   
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
    public async Task JoinQueue(string userEmail)
    {
        Player player = context.Players.Where(x => x.Email == userEmail).FirstOrDefault();
        Player player2 = null;  
        if (player != null)
        {
            _Players.Add(player);
        }
        else
            return;
        await Clients.All.SendAsync("QueuePlayerCountUpdate", _Players.Count);
        
        bool foundMatch = false;
        int eloRange = 50;
        while (!foundMatch) {
            if (_Players.Where(x => player.Elo - eloRange <= x.Elo && 
            x.Elo <= player.Elo + eloRange && 
            x.PlayerId != player.PlayerId).Any())
                player2 = _Players.Where(x => player.Elo - eloRange <= x.Elo 
&& x.Elo <=                  player.Elo + eloRange).FirstOrDefault();
            if(player2 == null)
            {
                Thread.Sleep(5000);
                eloRange += 50;
            }
            else
            {
                _Players.Remove(player);
                _Players.Remove(player2);
                foundMatch = true;
                Clients.Caller.SendAsync("MatchFound",player2.Email);
                Clients.User(player2.AspNetUserId).SendAsync("MatchFound", player.Email);
            } 
        }    
    }
}

这是Hub的后台代码。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace BackEnd_ASP.NET
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddControllers();
            services.AddEndpointsApiExplorer();
            services.AddSwaggerGen();
            services.AddSignalR();
            services.AddCors(options =>
            {
                var frontendURL = Configuration.GetValue<string>("frontend_url");
                options.AddDefaultPolicy(builder =>
                {
                    builder.WithOrigins("http://localhost:3000").AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                });
            });

            services.AddIdentity<IdentityUser, IdentityRole>().
                AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(
                options =>
                {
                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context =>
                        {
                            var accessToken = context.Request.Query["access_token"];

                            if (!string.IsNullOrEmpty(accessToken))
                            {
                                context.Token = accessToken;
                            }
                            return Task.CompletedTask;
                        }
                    };
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        ValidateAudience = false,
                        ValidateLifetime = false,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey
                        (Encoding.UTF8.GetBytes(Configuration["keyjwt"])),
                        ClockSkew = TimeSpan.Zero
                    };
                    
                });
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseRouting();

            app.UseCors();  
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<GameHub>("/GameHub");
            });
            app.UseAuthorization();
        }
    }
}

这是启动配置,以及连接初始化如下:

const connection = new HubConnectionBuilder().
            withUrl("https://localhost:7244/GameHub",{
                accessTokenFactory: ()=> {return `bearer ${token}`},  
                skipNegotiation: true,
                transport: HttpTransportType.WebSockets
              }).
            configureLogging(LogLevel.Information).build(); 

如果简单地删除Authorization标签,一切都按预期工作。我尝试将授权标签移到端点上,或者同时放在端点和整个Hub上,我还尝试删除令牌中的bearer,尝试删除skip negotiation选项,并尝试以几种方式修改OnMessageReceived,但都没有成功。我确保在IIS Windows服务器中启用了套接字。


<details>
<summary>英文:</summary>
I am using a SignlaR Hub to handle matchmaking for an online chess application, and I&#39;m trying to restrict usage of the hub with JWT authorization.

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class GameHub : Hub
{
private readonly ApplicationDbContext context;
public static List<Player> _Players = new List<Player>();

    public GameHub(ApplicationDbContext context)
{
this.context = context;
}
public async Task SendMessage(string user, string message)
{   
await Clients.All.SendAsync(&quot;ReceiveMessage&quot;, user, message);
}
public async Task JoinQueue(string userEmail)
{
Player player = context.Players.Where(x =&gt; x.Email == userEmail).FirstOrDefault();
Player player2 = null;  
if (player != null)
{
_Players.Add(player);
}
else
return;
await Clients.All.SendAsync(&quot;QueuePlayerCountUpdate&quot;, _Players.Count);
bool foundMatch = false;
int eloRange = 50;
while (!foundMatch) {
if (_Players.Where(x =&gt; player.Elo - eloRange &lt;= x.Elo &amp;&amp; 
x.Elo &lt;= player.Elo + eloRange &amp;&amp; 
x.PlayerId != player.PlayerId).Any())
player2 = _Players.Where(x =&gt; player.Elo - eloRange &lt;= x.Elo 

&& x.Elo <= player.Elo + eloRange).FirstOrDefault();
if(player2 == null)
{
Thread.Sleep(5000);
eloRange += 50;
}
else
{
_Players.Remove(player);
_Players.Remove(player2);
foundMatch = true;
Clients.Caller.SendAsync("MatchFound",player2.Email);
Clients.User(player2.AspNetUserId).SendAsync("MatchFound", player.Email);
}

        }
}
}
 this is the code behind the hub,

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace BackEnd_ASP.NET
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

    public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;
options.UseSqlServer(Configuration.GetConnectionString(&quot;DefaultConnection&quot;)));
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddSignalR();
services.AddCors(options =&gt;
{
var frontendURL = Configuration.GetValue&lt;string&gt;(&quot;frontend_url&quot;);
options.AddDefaultPolicy(builder =&gt;
{
builder.WithOrigins(&quot;http://localhost:3000&quot;).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
});
});
services.AddIdentity&lt;IdentityUser, IdentityRole&gt;().
AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
options =&gt;
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =&gt;
{
var accessToken = context.Request.Query[&quot;access_token&quot;];
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey
(Encoding.UTF8.GetBytes(Configuration[&quot;keyjwt&quot;])),
ClockSkew = TimeSpan.Zero
};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseCors();  
app.UseEndpoints(endpoints =&gt;
{
endpoints.MapControllers();
endpoints.MapHub&lt;GameHub&gt;(&quot;/GameHub&quot;);
});
app.UseAuthorization();
}
}

}

this is the startup 
and this is the connection initialization 

const connection = new HubConnectionBuilder().
withUrl("https://localhost:7244/GameHub",{
accessTokenFactory: ()=> {return bearer ${token}},
skipNegotiation: true,
transport: HttpTransportType.WebSockets
}).
configureLogging(LogLevel.Information).build();


If i simply remove the Authorization tag everything works as expected. I have tried moving the authorization tag to the endpoint, or having it both on the endpoint and the entire hub, i have also tried removing bearer from the token,i have tried removing the skip negotiation option,and tried modifying the   

OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];

                        if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
} 
in a couple of ways, none of which have worked. I have made sure to have sockets enabled in IIS Windows server.
</details>
# 答案1
**得分**: 1
请将您的代码更改如下,问题将得到解决。请查看官方文档:<br/>
[ASP.NET Core SignalR 中的身份验证和授权][1]<br/>
[中间件顺序][2]
```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseCors();  
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<GameHub>("/GameHub");
});
//app.UseAuthorization();
}
英文:

Change your code like below and the issue will be fixed. Please check the official doc : <br/>
Authentication and authorization in ASP.NET Core SignalR
<br/>

Middleware Order

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseCors();  
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =&gt;
{
endpoints.MapControllers();
endpoints.MapHub&lt;GameHub&gt;(&quot;/GameHub&quot;);
});
//app.UseAuthorization();
}

huangapple
  • 本文由 发表于 2023年6月13日 00:17:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76458529.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定