由于401, SignalR连接回到长轮询



我有一个。net 6后端和一个Angular 13后端,使用JWT令牌进行验证。由于某种原因,SignalR总是退回到长轮询,无论是在生产和开发机器上,似乎是呼叫协商?协商版本=1是成功的,它选择WebSockets,但之后它调用localhost:PORT/hubs/myhub?id=[id]&access_token=[JWTTOKEN]返回一个401.

Angular部分使用NGRX来获取JWT令牌,而JWT令牌在5分钟后过期。当它在连接建立后收到401时,它将断开连接,进行正常的renew呼叫,并使用新的JWT令牌重新连接。然而,上面描述的请求将始终返回401,即使有一个有效的令牌。

My SignalR service:

export class NotificationSignalrService {
private connection: signalR.HubConnection;
connectionClosedRefreshTokenSubscription: Subscription | undefined;
startConnectionRefreshTokenSubscription: Subscription | undefined;
constructor(@Inject(APP_CONFIG) private appConfig: any, private store: Store) {
this.connection = new signalR.HubConnectionBuilder()
.withUrl(`${this.appConfig.SIGNALR}/hubs/notificationhub`, this.hubConnectionOptions)
.configureLogging(signalR.LogLevel.Debug)
//.withAutomaticReconnect()
.build();
this.connection.onclose(error => {
console.log(`Forbindelse lukket pga: ${error}`);
this.store.dispatch(AuthActions.renewNoLoading());
this.connectionClosedRefreshTokenSubscription = this.store.select(AuthSelectors.selectTokenRefreshed).subscribe({
next: tokenRefreshed => {
if (tokenRefreshed) {
this.connectionClosedRefreshTokenSubscription?.unsubscribe();
this.startSignalRConnection();
}
}
})
});

this.startSignalRConnection();
this.startListening();
}
startSignalRConnection() {
this.connection.start().catch(error => {
console.log(`Der skete en fejl ved start af signalR ${error}`);
this.startConnectionRefreshTokenSubscription = this.store.select(AuthSelectors.selectTokenRefreshed).subscribe({
next: tokenRefreshed => {
if (tokenRefreshed) {
this.startConnectionRefreshTokenSubscription?.unsubscribe();
this.connection.start().catch(error => console.log(`Kunne ikke starte forbindelsen efter renew ${error}`));
}
}
})
});
}
@HostListener('window:beforeunload', ['$event'])
beforeunloadHandler() {
this.connection.stop();
}
protected get hubConnectionOptions(): IHttpConnectionOptions {
// NOTE: The auth token must be updated for each request. So using headers option is not true.
//       Also for websockets and some other protocols signalr cannot set auth headers.
//       See https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0#bearer-token-authentication
return {
/*headers,*/
accessTokenFactory: () => {
return this.store.select(AuthSelectors.getLoggedInToken)
.pipe(take(1), filter(x => x !== null), map(x => x === null ? "" : x)).toPromise();
// this.authService.refreshLogin()
//   .pipe(map(_ => this.authService.accessToken)).toPromise();
}
};
// NOTE:
// The access token function you provide is called before every HTTP request made by SignalR. If you need to renew the token in order to keep the connection active (because it may expire during the connection), do so from within this function and return the updated token.
// In standard web APIs, bearer tokens are sent in an HTTP header. However, SignalR is unable to set these headers in browsers when using some transports. When using WebSockets and Server - Sent Events, the token is transmitted as a query string parameter.
}
getAuthToken() {
let token = '';
this.store.select(AuthSelectors.getLoggedInToken).pipe(take(1))
.subscribe(authToken => token = authToken ?? "");
return {
Authorization: `Bearer ${token}`
};
}
startListening() {
this.connection.on("NewNotificationForUser", (notification: NotificationsEntity) =>
this.store.dispatch(NotificationsState.NotificationsActions.newNotification({ notification }))
);
}

在。net启动下我有services.AddSignalR();ConfigureServices

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/hubs/notificationhub");
});

inConfigure

My Hub有一个[Authorize]属性。

您可能没有处理access_token查询字符串参数。这在使用WebSockets时是必需的,因为WebSockets的浏览器api不支持设置标头。

文档解释了如何处理查询字符串https://learn.microsoft.com/aspnet/core/signalr/authn -和- authz?view=aspnetcore - 7.0 # built-in-jwt-authentication

最新更新