Flutter应用对接SignalR [ABP]时认证失败求助
Flutter SignalR 认证问题:后端接收Token但Hub连接isAuthenticated为false
问题描述
我在将Flutter应用连接到SignalR时遇到认证问题:后端服务器已正确接收Token,但Hub连接的isAuthenticated始终为false,尝试多种方法均未解决,寻求帮助。
使用的包
- signalr_netcore(基于master分支的Git仓库)
flutter doctor -v 输出
[√] Flutter (Channel master, 3.16.0-5.0.pre.15, on Microsoft Windows [Version 10.0.22621.2283], locale en-US) • Flutter version 3.16.0-5.0.pre.15 on channel master at C:\src\flutter • Framework revision fe0275f9d5 (22 hours ago), 2023-10-05 05:57:26 -0400 • Engine revision f8d53c5fe9 • Dart version 3.2.0 (build 3.2.0-231.0.dev) • DevTools version 2.28.1 [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 33.0.2) • Android SDK at C:\Users\muonroi\AppData\Local\Android\Sdk • Platform android-33-ext5, build-tools 33.0.2 • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-9505619) • All Android licenses accepted. [√] Chrome - develop for the web • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe [√] Visual Studio - develop Windows apps (Visual Studio Enterprise 2022 17.6.5) • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Enterprise • Visual Studio Enterprise 2022 version 17.6.33829.357 • Windows 10 SDK version 10.0.22000.0 [√] Android Studio (version 2022.1) • Android Studio at C:\Program Files\Android\Android Studio • Flutter plugin可安装 • Dart plugin可安装 • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-9505619) [√] VS Code (version 1.83.0) • VS Code at C:\Users\muonroi\AppData\Local\Programs\Microsoft VS Code • Flutter extension version 3.74.0 [√] Connected device (4 available) • Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22621.2283] • Chrome (web) • chrome • web-javascript • Google Chrome 117.0.5938.134 • Edge (web) • edge • web-javascript • Microsoft Edge 117.0.2045.47 [√] Network resources • All expected network resources are available. • No issues found!
Flutter Hub连接代码
// Flutter code to connect to the hub await _initSharedPreferences(); _hubConnection = HubConnectionBuilder() .withUrl( SignalrCentral.chatStatus, options: HttpConnectionOptions( accessTokenFactory: () async { return _sharedPreferences .getString(KeyToken.accessToken.name)!; }, headers: SignalrCentral.getHeader( _sharedPreferences.getString(KeyToken.encToken.name)!))) .withAutomaticReconnect(retryDelays: [30000]) .build(); await initHubAndListenStatus(); Future<void> initHubAndListenStatus() async { try { await _hubConnection.start(); debugPrint('Connection established successfully'); } catch (e) { print('Error starting hub connection: $e'); } }
后端服务器认证代码
/* This method is needed to authorize SignalR javascript client. * SignalR can not send authorization header. So, we are getting it from query string as an encrypted text. */ private static Task QueryStringTokenResolver(MessageReceivedContext context) { if (context.HttpContext.Request.Path.Value.StartsWith("/signalr")) { var env = context.HttpContext.RequestServices.GetService<IWebHostEnvironment>(); var config = env.GetAppConfiguration(); var allowAnonymousSignalRConnection = bool.Parse(config["App:AllowAnonymousSignalRConnection"]); return SetToken(context, allowAnonymousSignalRConnection); } List<string> urlsUsingEnchAuthToken = new List<string>() { "/Chat/GetUploadedObject?", "/Profile/GetProfilePictureByUser?" }; if (urlsUsingEnchAuthToken.Any(url => context.HttpContext.Request.GetDisplayUrl().Contains(url))) { if (context.HttpContext.Request.Headers.ContainsKey("authorization")) { return Task.CompletedTask; } return SetToken(context, false); } return Task.CompletedTask; } private static Task SetToken(MessageReceivedContext context, bool allowAnonymous) { var qsAuthToken = context.HttpContext.Request.Query["enc_auth_token"].FirstOrDefault() ?? context.HttpContext.Request.Headers["enc_auth_token"].FirstOrDefault(); if (qsAuthToken == null) { if (!allowAnonymous) { throw new AbpAuthorizationException("SignalR auth token is missing."); } return Task.CompletedTask; } //Set auth token from cookie context.Token = SimpleStringCipher.Instance.Decrypt(qsAuthToken, AppConsts.DefaultPassPhrase); return Task.CompletedTask; }
排查与解决方案
1. 匹配Token传递方式
后端QueryStringTokenResolver优先从**查询字符串enc_auth_token或请求头enc_auth_token**获取加密Token,但当前Flutter代码的传递方式不匹配:
accessTokenFactory返回的明文Token会被自动放入Authorization: Bearer {token}头,后端未处理该头的SignalR认证逻辑- 需调整Flutter代码,将加密Token放到后端期望的位置:
// 方式1:通过查询参数传递 _hubConnection = HubConnectionBuilder() .withUrl( "${SignalrCentral.chatStatus}?enc_auth_token=${Uri.encodeComponent(_sharedPreferences.getString(KeyToken.encToken.name)!)}", options: HttpConnectionOptions( // 若后端支持明文Token可保留,否则删除 accessTokenFactory: () async { return _sharedPreferences.getString(KeyToken.accessToken.name)!; }, )) .withAutomaticReconnect(retryDelays: [30000]) .build(); // 方式2:通过请求头传递 _hubConnection = HubConnectionBuilder() .withUrl( SignalrCentral.chatStatus, options: HttpConnectionOptions( headers: { "enc_auth_token": _sharedPreferences.getString(KeyToken.encToken.name)! }, )) .withAutomaticReconnect(retryDelays: [30000]) .build();
2. 验证Token解密与身份验证流程
- 在后端
SetToken方法中添加日志,输出解密后的context.Token,确认其格式、签名、有效期均正确 - 检查系统身份验证中间件(如JWT)是否能正确解析该Token,确保用户身份信息被正确注入
- 确认SignalR Hub已添加
[Authorize]特性,且认证方案已在AddSignalR中配置:
services.AddSignalR(options => { options.AuthorizationEnabled = true; });
3. 检查SignalR中间件顺序
确保身份验证中间件在SignalR中间件之前注册:
app.UseAuthentication(); app.UseAuthorization(); app.UseSignalR(routes => { routes.MapHub<ChatHub>("/signalr/chat"); });
4. 启用客户端日志排查
在Flutter中添加SignalR连接日志,捕获握手阶段的错误信息:
_hubConnection = HubConnectionBuilder() .withUrl(...) .build(); // 添加状态监听 _hubConnection.onclose((error) => print("Connection closed: $error")); _hubConnection.onreconnecting((error) => print("Reconnecting: $error")); // 调用授权方法验证权限 try { await _hubConnection.start(); debugPrint('Connection ID: ${_hubConnection.connectionId}'); // 调用一个需要认证的Hub方法,触发权限检查 await _hubConnection.invoke("GetUserStatus"); } catch (e) { print('Connection error: ${e.toString()}'); }
内容的提问来源于stack exchange,提问作者Muon Roi
相关产品推荐
相关产品推荐

