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

如何在Flutter的HTTP插件中为所有API请求配置SSL固定

使用Flutter HTTP插件实现SSL固定的方法与示例

核心思路

SSL固定的本质是在请求时校验服务器证书的合法性,避免中间人攻击。对于HTTP插件,我们需要自定义BaseClient,通过重写证书校验逻辑实现固定。


步骤1:获取服务器证书的SHA-256哈希值

先拿到你API服务器证书的指纹,用以下命令(替换成你的域名):

openssl s_client -connect your-api-domain.com:443 | openssl x509 -noout -fingerprint -sha256

输出结果类似SHA256 Fingerprint=AB:CD:EF...,去掉所有冒号,得到纯大写的哈希字符串备用。


步骤2:基于HTTP插件实现指纹校验

自定义客户端类,重写证书校验逻辑:

import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;

// 替换成你的服务器证书SHA-256哈希(去掉冒号)
const String _trustedCertSha256 = "ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890";

class SslPinningClient extends http.BaseClient {
  final http.Client _inner = http.Client();

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    if (request.url.scheme == 'https') {
      final client = HttpClient();
      // 自定义证书校验逻辑
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        // 计算当前证书的SHA-256哈希
        final certSha256 = sha256.convert(cert.der).toString().toUpperCase();
        // 对比信任的哈希值,一致则通过校验
        return certSha256 == _trustedCertSha256;
      };

      try {
        final httpRequest = await client.openUrl(request.method, request.url);
        // 复制原请求的头信息
        request.headers.forEach((key, value) => httpRequest.headers.add(key, value));
        // 写入请求体
        if (request is http.Request && request.body.isNotEmpty) {
          httpRequest.write(request.body);
        }
        final httpResponse = await httpRequest.close();
        // 转换为HTTP插件的响应格式
        final bodyStream = httpResponse.transform(utf8.decoder);
        final body = await bodyStream.join();
        return http.StreamedResponse(
          Stream.value(utf8.encode(body)),
          httpResponse.statusCode,
          headers: httpResponse.headers,
          reasonPhrase: httpResponse.reasonPhrase,
        );
      } finally {
        client.close();
      }
    }
    // 非HTTPS请求直接用原客户端处理
    return _inner.send(request);
  }
}

// 使用示例
void main() async {
  final client = SslPinningClient();
  try {
    final response = await client.get(Uri.parse('https://your-api-domain.com/api/test'));
    print('响应状态码: ${response.statusCode}');
    print('响应内容: ${response.body}');
  } catch (e) {
    print('请求失败: $e');
  } finally {
    client.close();
  }
}

可选:嵌入完整证书文件校验

如果需要更严格的校验,可以直接把服务器证书(.pem格式)放到项目assets中,对比完整证书内容:

import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;

class SslPinningClient extends http.BaseClient {
  final http.Client _inner = http.Client();
  late List<int> _trustedCertBytes;

  SslPinningClient() {
    _loadTrustedCert();
  }

  // 从assets加载信任的证书文件(需在pubspec.yaml中配置assets路径)
  Future<void> _loadTrustedCert() async {
    final certString = await rootBundle.loadString('assets/trusted_cert.pem');
    _trustedCertBytes = utf8.encode(certString);
  }

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    if (request.url.scheme == 'https') {
      final client = HttpClient();
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        // 对比证书的原始字节内容
        return jsonEncode(cert.der) == jsonEncode(_trustedCertBytes);
      };

      // 后续请求逻辑和指纹校验版本一致,此处省略...
      try {
        final httpRequest = await client.openUrl(request.method, request.url);
        request.headers.forEach((key, value) => httpRequest.headers.add(key, value));
        if (request is http.Request && request.body.isNotEmpty) {
          httpRequest.write(request.body);
        }
        final httpResponse = await httpRequest.close();
        final bodyStream = httpResponse.transform(utf8.decoder);
        final body = await bodyStream.join();
        return http.StreamedResponse(
          Stream.value(utf8.encode(body)),
          httpResponse.statusCode,
          headers: httpResponse.headers,
          reasonPhrase: httpResponse.reasonPhrase,
        );
      } finally {
        client.close();
      }
    }
    return _inner.send(request);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:01:40