英文:
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'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("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);
}
}
}
}
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<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();
}
}
}
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/>
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();
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论