Flutter中WebSocket连接证书验证失败问题解决方案咨询
Flutter WebSocket自签名证书验证失败解决方案
问题根源分析
你当前的代码存在两个核心问题:
- 自定义的
HttpClient创建后未实际传递给IOWebSocketChannel,导致WebSocket握手仍使用系统默认客户端,证书校验规则未生效 HttpOverrides.global = null在finally块中过早执行,此时WebSocket握手可能尚未完成,全局覆盖被提前重置
解决方案1:正确传递自定义HttpClient给WebSocket
修改IOWebSocketChannel.connect调用,通过customClient参数传入你配置了badCertificateCallback的HttpClient:
Future<List<Acumulado>?> _connectToWebSocket(String id) async { Completer<List<Acumulado>> completer = Completer(); List<Acumulado> maquinas = []; try { final String webSocketUrl = '${widget.constantes.appSettings!.address.replaceFirst("http", "ws")}:8887/${widget.nombrebd}'; // 创建自定义HTTP客户端以接受自签名证书 final httpClient = HttpClient() ..badCertificateCallback = (X509Certificate cert, String host, int port) => true; // 关键:将自定义HttpClient传入WebSocket连接 _channel = IOWebSocketChannel.connect( Uri.parse(webSocketUrl), headers: {'session': widget.constantes.session, 'authenticate': 'true'}, customClient: httpClient, // 新增这一行 ); _channel.sink.add('{"clase":"request","datos":[{}]}'); _channel.stream.listen((message) { var data = json.decode(message); print("data: $data $message"); if (data['clase'] == 'estado') { var message = data['datos'][0]['mensaje']; print(message); maquinas = (data['datos'] .map<Acumulado>((item) => Acumulado.fromJson(item)) .toList()); _channel.sink.add('{"clase":"request","datos":[{}]}'); completer.complete(maquinas); } }); } catch (e) { print('Error connecting to WebSocket: $e'); completer.completeError(e); } return completer.future; }
解决方案2:正确使用HttpOverrides全局覆盖
如果选择全局禁用证书校验,需在WebSocket握手完成前保持HttpOverrides生效,不要在finally中过早重置:
- 在应用启动时全局设置(推荐,避免局部异步时序问题):
void main() { HttpOverrides.global = MyHttpOverrides(); runApp(const YourApp()); }
- 若仅需局部生效,需确保在WebSocket连接完成后再重置覆盖:
Future<List<Acumulado>?> _connectToWebSocket(String id) async { Completer<List<Acumulado>> completer = Completer(); List<Acumulado> maquinas = []; // 提前设置全局覆盖 HttpOverrides.global = MyHttpOverrides(); try { final String webSocketUrl = '${widget.constantes.appSettings!.address.replaceFirst("http", "ws")}:8887/${widget.nombrebd}'; _channel = IOWebSocketChannel.connect( Uri.parse(webSocketUrl), headers: {'session': widget.constantes.session, 'authenticate': 'true'}, ); _channel.sink.add('{"clase":"request","datos":[{}]}'); _channel.stream.listen((message) { var data = json.decode(message); print("data: $data $message"); if (data['clase'] == 'estado') { var message = data['datos'][0]['mensaje']; print(message); maquinas = (data['datos'] .map<Acumulado>((item) => Acumulado.fromJson(item)) .toList()); _channel.sink.add('{"clase":"request","datos":[{}]}'); completer.complete(maquinas); // 连接完成后再重置覆盖(可选) HttpOverrides.global = null; } }); } catch (e) { print('Error connecting to WebSocket: $e'); completer.completeError(e); HttpOverrides.global = null; } return completer.future; }
解决方案3:信任特定自签名证书(更安全)
完全禁用证书校验存在安全风险,建议将服务器的自签名证书导入应用,仅信任该证书:
- 将证书文件(如
server_cert.pem)放入assets目录,并在pubspec.yaml中声明:
assets: - assets/server_cert.pem
- 加载证书并配置
SecurityContext:
Future<List<Acumulado>?> _connectToWebSocket(String id) async { Completer<List<Acumulado>> completer = Completer(); List<Acumulado> maquinas = []; try { final String webSocketUrl = '${widget.constantes.appSettings!.address.replaceFirst("http", "ws")}:8887/${widget.nombrebd}'; // 加载自签名证书 ByteData certData = await rootBundle.load('assets/server_cert.pem'); SecurityContext securityContext = SecurityContext.defaultContext; securityContext.setTrustedCertificatesBytes(certData.buffer.asUint8List()); // 使用带信任证书的HttpClient final httpClient = HttpClient(context: securityContext); _channel = IOWebSocketChannel.connect( Uri.parse(webSocketUrl), headers: {'session': widget.constantes.session, 'authenticate': 'true'}, customClient: httpClient, ); // 后续监听逻辑不变... _channel.sink.add('{"clase":"request","datos":[{}]}'); _channel.stream.listen((message) { var data = json.decode(message); print("data: $data $message"); if (data['clase'] == 'estado') { var message = data['datos'][0]['mensaje']; print(message); maquinas = (data['datos'] .map<Acumulado>((item) => Acumulado.fromJson(item)) .toList()); _channel.sink.add('{"clase":"request","datos":[{}]}'); completer.complete(maquinas); } }); } catch (e) { print('Error connecting to WebSocket: $e'); completer.completeError(e); } return completer.future; }
内容的提问来源于stack exchange,提问作者yellowman
相关产品推荐
相关产品推荐

