ProxyPin Dio HttpClient 配置 开发者视角
两种思路
抓 Flutter App 的 HTTPS 流量有两种方式:
| 方式 | 场景 | 改动位置 |
|---|---|---|
| ProxyPin 自动绕过 | 抓别人的 App | 不改任何代码 |
| App 显式信任 | 抓自己开发的 App | Dart 代码 |
自己开发的 App 用第二种方式更可控——只在 debug 构建中配置,release 构建保持证书固定不变。
HttpClient 配置(dart:io)
Dart 内置的 HttpClient:
import 'dart:io';
HttpClient createHttpClient() {
final context = SecurityContext.defaultContext;
// 开发模式:信任 ProxyPin 的根证书
if (kDebugMode) {
// 方式一:信任所有(不推荐,太宽松)
// HttpClient()..badCertificateCallback = (cert, host, port) => true;
// 方式二:导入 ProxyPin 根证书(推荐)
final proxyPinCert = File('assets/proxypin-ca.pem').readAsBytesSync();
context.setTrustedCertificatesBytes(proxyPinCert);
}
return HttpClient(context: context);
}
Dio 配置
Dio 是 Flutter 生态最流行的 HTTP 客户端:
import 'package:dio/dio.dart';
Dio createDio() {
// 开发模式信任所有证书(最简单)
if (kDebugMode) {
return Dio(BaseOptions(
baseUrl: 'https://api.myapp.com',
))..interceptors.add(PiniaInterceptor(
// debug 模式禁用证书固定
enable: false,
));
}
// release 模式正常固定
return Dio(BaseOptions(
baseUrl: 'https://api.myapp.com',
))..interceptors.add(PiniaInterceptor(
certs: [prodCertBytes],
));
}
Dio + ProxyPin 的最佳实践
Dio createDioForDebug() {
final dio = Dio();
// debug 构建:接受 ProxyPin 的代理证书
dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
// 方式一:信任所有(debug only)
if (kDebugMode) {
client.badCertificateCallback = (cert, host, port) => true;
}
return client;
},
);
return dio;
}
从 ProxyPin 导出根证书
要导入到 App 的 ProxyPin 根证书:
- 打开 ProxyPin 桌面端
- 「证书」→「导出证书」
- 选择 PEM 格式保存
- 放入 Flutter 项目的
assets/目录 - 在
pubspec.yaml中声明 assets 路径 - 运行时读取并导入 SecurityContext
开发 vs 生产分离的重要性
绝对不要把 debug 证书信任逻辑带到生产构建中:
// ❌ 错误:生产环境也信任所有证书
client.badCertificateCallback = (cert, host, port) => true;
// ✅ 正确:仅 debug 模式
if (kDebugMode) {
client.badCertificateCallback = (cert, host, port) => true;
}
或者用构建配置:
// flutter run --dart-define=ALLOW_PROXY=true
bool get allowProxy => const String.fromEnvironment('ALLOW_PROXY') == 'true';
配合 ProxyPin 功能
配置好 App 信任 ProxyPin 后,可以用全部 ProxyPin 功能:
- 请求重写(改 Header、改 URL)
- JS 脚本(动态处理请求响应)
- AES 解密(App 加密过的请求体)
- HAR 导出(分享给后端排查)
与原生 App 调试体验完全一致。