You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为本地SignalR Web服务器启用SSL,解决Flutter连接异常?

问题:Flutter连接本地SignalR服务器的SSL配置问题

初始连接尝试及错误

尝试在Flutter中通过以下代码连接本地SignalR服务器:

final connection = HubConnectionBuilder().withUrl('https://localhost:44320/chat-hub',
      HttpConnectionOptions(
        logging: (level, message) => print(message),
      )).build();

await connection.start();

connection.on('ReceiveMessage', (message) {
  print(message.toString());
});

握手成功并收到服务器消息后,程序崩溃,报错:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: The remote computer refused the network connection.

#0 IOClient.send (package:http/src/io_client.dart:94:7)

#1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:93:32)

#2 _withClient (package:http/http.dart:166:12)

#3 _MainPageState.initState. (package:multipanel_chatwoot/presentation/pages/home_page.dart:22:24)

替换为IP地址后的错误

将localhost替换为本地IPhttps://192.168.1.39:44320/chat-hub后,协商请求出现证书验证错误:

flutter: Failed to complete negotiation with the server: HandshakeException: Handshake error in client (OS Error:
CERTIFICATE_VERIFY_FAILED: Hostname mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393))
flutter: Failed to start the connection: HandshakeException: Handshake error in client (OS Error:
CERTIFICATE_VERIFY_FAILED: Hostname mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393))
flutter: HubConnection failed to start successfully because of error '{HandshakeException: Handshake error in client (OS Error:
CERTIFICATE_VERIFY_FAILED: Hostname mismatch(../../third_party/boringssl/src/ssl/handshake.cc:393)).toString()}'.

改用HTTP协议后的错误

改用HTTP协议连接时,报错:

flutter: Failed to complete negotiation with the server: Connection closed before full header was received

添加证书忽略代码后的错误

添加本地开发用的证书忽略代码:

// in main()
HttpOverrides.global = MyHttpOverrides();

class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext? context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  }
}

后出现错误:

flutter: Failed to start the connection: Exception: Unexpected status code returned from negotiate '400'

尝试跳过协商后的错误

在HttpConnectionOptions中添加skipNegotiation: true,报错:

flutter: Failed to start the connection: Exception: Negotiation can only be skipped when using the WebSocket transport directly.


解决方案:为本地SignalR服务器配置有效SSL证书

1. 生成信任的本地SSL证书

如果是ASP.NET Core SignalR服务器,可使用dotnet dev-certs工具生成并信任本地证书:

  • 生成证书:
    dotnet dev-certs https -ep $HOME/.aspnet/https/aspnetapp.pfx -p <自定义密码>
    
  • 信任证书:
    dotnet dev-certs https --trust
    
    Windows/macOS会自动弹出信任确认窗口,按提示完成操作即可。

若需要包含本地IP和localhost的证书(避免主机名不匹配),可使用OpenSSL生成带SAN字段的证书:

openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes \
  -keyout localhost.key -out localhost.crt -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:192.168.1.39"

再转换为PFX格式供Kestrel使用:

openssl pkcs12 -export -out aspnetapp.pfx -inkey localhost.key -in localhost.crt

2. 配置SignalR服务器使用证书

在ASP.NET Core项目的appsettings.json中添加证书配置:

{
  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "aspnetapp.pfx",
        "Password": "<之前设置的密码>"
      }
    }
  }
}

或者在Program.cs中直接配置Kestrel:

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(44320, options =>
    {
        options.UseHttps("aspnetapp.pfx", "<密码>");
    });
});

3. Flutter端正确加载信任证书

  • 将生成的localhost.crt放入Flutter项目的assets目录,在pubspec.yaml中声明:
    assets:
      - assets/localhost.crt
    
  • 编写代码加载证书到SecurityContext:
    Future<SecurityContext> getSecurityContext() async {
      final certBytes = await rootBundle.load('assets/localhost.crt');
      final context = SecurityContext.defaultContext;
      context.setTrustedCertificatesBytes(certBytes.buffer.asUint8List());
      return context;
    }
    
  • 连接时使用该SecurityContext:
    final connection = HubConnectionBuilder().withUrl(
      'https://192.168.1.39:44320/chat-hub',
      HttpConnectionOptions(
        logging: (level, message) => print(message),
        httpClientAdapter: IOHttpClientAdapter(
          createHttpClient: () async => HttpClient(context: await getSecurityContext()),
        ),
      ),
    ).build();
    

4. 本地开发临时绕过证书验证(仅测试用)

如果不想配置证书,可直接指定WebSocket传输并跳过协商,同时保留证书忽略代码:

final connection = HubConnectionBuilder().withUrl(
  'wss://192.168.1.39:44320/chat-hub',
  HttpConnectionOptions(
    logging: (level, message) => print(message),
    skipNegotiation: true,
    transport: HttpTransportType.webSockets,
  ),
).build();

注意:此方式仅适用于本地开发,生产环境绝对禁止使用。


内容的提问来源于stack exchange,提问作者Royal_Scribblz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 22:38:11