Angular11中如何通过Azure Function的SignalR协商实现token过期后续权
解决方案
问题根因
你当前的代码中accessTokenFactory返回的是第一次调用协商接口获取的静态token,Azure SignalR Service默认返回的该类token有效期为1小时,过期后无论是重连还是轮询类传输请求都会返回401未授权错误。
修复步骤
前端signalr.service.ts修改
核心调整逻辑:将accessTokenFactory改为异步函数,每次SignalR需要验证token时(包括初始握手、重连、轮询请求校验)主动拉取最新的有效token,同时优化连接初始化逻辑避免重复创建连接。
修改后代码如下:
import * as signalR from '@microsoft/signalr'; import { Subject, firstValueFrom } from 'rxjs'; // 导入firstValueFrom把Observable转Promise orderMessages: Subject<any> = new Subject(); saleMessages: Subject<any> = new Subject(); hubConnection?: signalR.HubConnection; private getConnectionInfo(): Observable<SignalRConnectionInfo> { return this.http.get<SignalRConnectionInfo>('http://localhost:7071/api/SignalRNegotiationFunction'); } async init() { // 避免重复初始化连接 if (this.hubConnection) { await this.hubConnection.stop(); } // 首次拉取连接信息获取SignalR服务端点 const firstInfo = await firstValueFrom(this.getConnectionInfo()); let options = { // accessTokenFactory支持返回Promise,每次需要token时自动拉取最新协商结果 accessTokenFactory: async () => { const info = await firstValueFrom(this.getConnectionInfo()); return info.accessToken; } }; this.hubConnection = new signalR.HubConnectionBuilder() .withUrl(firstInfo.url, options) .withAutomaticReconnect([0, 1000, 5000, 10000, 30000]) // 可调整重试策略,移除null避免停止重试 .configureLogging(signalR.LogLevel.Information) .build(); // 重连成功回调可选,按需添加业务逻辑 this.hubConnection.onreconnected(() => { console.log('SignalR重连成功,已自动刷新token'); }); // 重连失败回调可选 this.hubConnection.onclose((err) => { console.error('SignalR连接关闭', err); // 按需添加重试初始化逻辑 }); this.hubConnection.off('notifyresult'); this.hubConnection.on('notifyresult', (data: any) => { if(data == "Orders") { this.orderMessages.next(data); } else { this.saleMessages.next(data); } }); try { await this.hubConnection.start(); } catch (err) { console.error('SignalR启动失败', err.toString()); } }
可选优化:添加token缓存减少接口请求
如果需要避免每次token校验都调用协商接口,可以添加缓存逻辑,提前5分钟刷新token即可:
private cachedToken?: string; private tokenExpireTime?: number; private async getValidToken(): Promise<string> { // 缓存未过期直接返回缓存的token,提前5分钟刷新 if (this.cachedToken && Date.now() < (this.tokenExpireTime || 0) - 5 * 60 * 1000) { return this.cachedToken; } const info = await firstValueFrom(this.getConnectionInfo()); this.cachedToken = info.accessToken; // 协商接口返回的token默认有效期1小时,设置过期时间 this.tokenExpireTime = Date.now() + 60 * 60 * 1000; return this.cachedToken; } // 对应的accessTokenFactory修改为调用该方法 accessTokenFactory: () => this.getValidToken()
后端Azure Function优化建议
你当前的协商接口设置为匿名权限,生产环境建议添加业务身份校验,只有合法登录的用户才能调用接口获取token,避免未授权用户滥用SignalR服务。
注意事项
- WebSocket协议下token仅在握手阶段校验,连接保持期间不会校验token有效期,只有断开重连时才会拉取新token,上述方案已经覆盖该场景
- 长轮询、SSE传输协议下每次请求都会校验token,上述异步拉取新token的逻辑可以自动处理过期问题,不需要额外手动刷新连接
内容的提问来源于stack exchange,提问作者RGS
相关产品推荐
相关产品推荐

