SignalR集成AzureAD认证后WebSocket降级为长轮询问题求助
SignalR + Azure AD 认证降级排查思路
1. 先解决OnMessageReceived未触发的问题
- 检查认证中间件顺序:必须保证
app.UseAuthentication()放在app.UseRouting()和app.UseEndpoints()之前,否则SignalR请求到达Hub时认证逻辑还未执行,自然触发不了该事件。 - 验证路径匹配逻辑:在
OnMessageReceived里添加日志输出context.HttpContext.Request.Path和SomeHub.Path的实际值,确认StartsWithSegments是否真的匹配上了Hub的路由路径。 - 核对认证Scheme一致性:Hub的
[Authorize]指定了JwtBearerDefaults.AuthenticationScheme,确保配置里的JWT Scheme(代码中为"Bearer")和这个完全一致,没有其他认证Scheme(比如Cookie)优先拦截请求。
2. 修正客户端SignalR配置参数
你当前的withUrl参数顺序错误,第二个参数应该是一个配置对象,而非拆分的布尔值和枚举,这会导致令牌传递配置失效:
let optimizerConnection = new HubConnectionBuilder() .withUrl("/hubpath", { accessTokenFactory: () => getRawJwtToken(), skipNegotiation: false, transport: HttpTransportType.WebSockets }) .withAutomaticReconnect() .configureLogging(LogLevel.Trace) .build()
修改后用浏览器F12查看:
- 协商请求(
/hubpath/negotiate)的查询参数中是否携带access_token - WebSocket连接请求的URL里是否包含
access_token参数(WebSocket不支持自定义请求头,SignalR默认用查询字符串传递令牌)
3. 调整服务端JWT事件配置
不要直接通过new JwtBearerEvents()覆盖默认事件,这会丢失Microsoft Identity Web Api的内置逻辑,应采用追加事件的方式:
private static void ConfigureJwtBearerOptions(JwtBearerOptions options) { // 仅追加OnMessageReceived,保留原有默认事件 options.Events.OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubpath")) { context.Token = accessToken; } return Task.CompletedTask; }; // 其他事件单独赋值,不替换整个Events对象 options.Events.OnChallenge = OnChallenge; options.Events.OnTokenValidated = OnTokenValidated; options.Events.OnAuthenticationFailed = OnAuthenticationFailed; options.Events.OnForbidden = OnForbidden; }
4. 排查连接降级的具体原因
- 开启SignalR诊断日志,在
appsettings.json中添加:
日志会明确显示WebSocket被降级的原因,比如认证失败、协议不支持等。{ "Logging": { "LogLevel": { "Microsoft.AspNetCore.SignalR": "Debug", "Microsoft.AspNetCore.Http.Connections": "Debug" } } } - 检查协商请求状态:如果
/hubpath/negotiate返回401,SignalR会直接降级传输方式。确保协商请求能通过认证,它是HTTP请求,可以携带Authorization头。 - 验证令牌权限:用JWT解析工具检查令牌内是否包含
Optimizer角色,角色名称大小写要和[Authorize]中的完全一致。
内容的提问来源于stack exchange,提问作者Emmanuel DURIN
相关产品推荐
相关产品推荐

