如何在执行 POST 请求时解决颤振 CERTIFICATE_VERIFY_FAILED 错误?
Posted
技术标签:
【中文标题】如何在执行 POST 请求时解决颤振 CERTIFICATE_VERIFY_FAILED 错误?【英文标题】:how to solve flutter CERTIFICATE_VERIFY_FAILED error while performing a POST request? 【发布时间】:2019-06-14 13:26:25 【问题描述】:我正在 Dart 中发送一个发布请求。当我在 Postman 等 API 测试工具上对其进行测试时,它会给出响应。但是当我运行应用程序时。它给了我以下错误:-
E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363))
这是我的函数代码 -
Future getAccessToken(String url) async
try
http.post('url',
body:
"email": "xyz@xyz.com",
"password": "1234"
).then((response)
print("Reponse status : $response.statusCode");
print("Response body : $response.body");
var myresponse = jsonDecode(response.body);
String token = myresponse["token"];
);
catch (e)
print(e.toString());
这是完整的错误正文:
E/flutter ( 6264): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception: E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363)) E/flutter ( 6264): #0 IOClient.send (package:http/src/io_client.dart:33:23) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #2 BaseClient.post (package:http/src/base_client.dart:54:7) E/flutter ( 6264): #3 post.<anonymous closure> (package:http/http.dart:70:16) E/flutter ( 6264): #4 _withClient (package:http/http.dart:166:20) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #5 post (package:http/http.dart:69:5) E/flutter ( 6264): #6
_MyLoginFormState.getAccessToken (package:chart/main.dart:74:7) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #7
_MyLoginFormState.build.<anonymous closure> (package:chart/main.dart:64:29)
【问题讨论】:
您是否使用自签名证书? 我只想接受所有证书 好吧,如果它是自签名的,它将无法工作。 Dart 不允许使用自签名证书。一种解决方案(恕我直言不好)是允许证书,即使是无效的,但它删除了使用证书的核心原则。 检查这个answer它对我来说非常有效 定义一个类 import 'dart:io';将“包:http/http.dart”导入为 http;导入“包:http/io_client.dart”;类 TrustAllCertificates 静态 http.Client sslClient() var ioClient = new HttpClient() ..badCertificateCallback = (X509Certificate cert, String host, int port) return (host.compareTo("domain-name.com") == 0 ); ; http.Client _client = IOClient(ioClient);返回_client; 【参考方案1】:为了让 Flutter/Dart 的新手更清楚,为了在项目中全局启用此选项,您需要执行以下操作:
-
在您的 main.dart 文件中,添加或导入以下类:
class MyHttpOverrides extends HttpOverrides @override HttpClient createHttpClient(SecurityContext? context) return super.createHttpClient(context) ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
-
在您的主函数中,在函数定义后添加以下行:
HttpOverrides.global = MyHttpOverrides();
This 评论对解决这个问题很有帮助,请注意...
这应该在开发模式下使用,不当你想发布到生产环境时这样做,这个答案的目的是强> 让开发对您来说更容易一些,对于生产,您需要解决您的证书问题并正确使用它,请查看其他答案,因为它可能对您的情况有所帮助。
【讨论】:
这帮助我使用了第三方库,该库不提供对内部 http 客户端实例的访问权限。现在可以了,谢谢 此解决方案完美运行。就我而言,这是典型的场景:开发盒上的自签名证书。 这会起作用,但它实际上会禁用 BoringSSL 中的安全性。如果您有充分的理由这样做(在技术上无效的开发盒上的自签名证书),那很好。但是在将这样的代码发送到生产环境时要小心...... 不要忘记使用 HttpClientcreateHttpClient(SecurityContext? context)
(注意问号)来保证空值安全
对于空安全用户添加?在 SecurityContext 之后,例如 SecurityContext?
【参考方案2】:
2021 年 2 月编辑和更新:早前提出此问题时,没有足够的文档和开发人员来回答。以下答案可能比这个更有帮助: Ma'moon Al-Akash Answer, Pedro Massango's Answer & Ken's Answer
如果你在这3个答案中都没有找到解决办法,可以试试下面的解决办法。
最初于 2019 年 1 月回答: 正如我发现的那样,正确(但不好)的方法是允许所有证书。
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
String url ='xyz@xyz.com';
Map map =
"email" : "email" ,
"password" : "password"
;
HttpClientRequest request = await client.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(map)));
HttpClientResponse response = await request.close();
String reply = await response.transform(utf8.decoder).join();
print(reply);
【讨论】:
一年后有没有机会找到更好的解决方案? 此问题仍然存在。如果不允许所有证书,如何在生产中处理它? 【参考方案3】:如果您使用的是Dio library,请执行以下操作:
Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(HttpClient client)
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
return client;
;
【讨论】:
我得到了:The name 'DefaultHttpClientAdapter' isn't a type, so it can't be used in an 'as' expression.
@Zorro 您对此有什么解决方案吗?我面临同样的例外。
@aanal-mehta 是的,看看我在底部的帖子。
@Zorro 可以分享链接吗?
@aanal-mehta 我的意思是***.com/questions/54285172/…【参考方案4】:
从https://letsencrypt.org/certs/lets-encrypt-r3.pem下载证书
将此文件添加到 assets/ca/ Flutter 项目根目录
在 pubspec.yaml 中添加 assets/ca/ assets 目录
在您的应用初始化时添加此代码:
void main() async
WidgetsFlutterBinding.ensureInitialized();
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
runApp(MyApp());
它适用于默认链,因此不需要在服务器上进行任何更改。 android
【讨论】:
太棒了? +100。像魅力一样工作 这是完美的工作。 应该将此解决方案用于新设备。请参考letsencrypt的更新:letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/… 另外,我可以使用我的cert.pem
代替过期的lets-encrypt-r3.pem
。【参考方案5】:
此代码对我有用
class MyHttpOverrides extends HttpOverrides
@override
HttpClient createHttpClient(SecurityContext context)
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
void main()
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
我想你也一样……
【讨论】:
这适用于移动设备,但不适用于 Flutter 网络【参考方案6】:最好的方法(我认为是这样)是允许受信任主机的证书,所以如果您的 API 主机是 "api.my_app"
,您可以只允许来自该主机的证书:
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port)
final isValidHost = host == "api.my_app";
// Allowing multiple hosts
// final isValidHost = host == "api.my_app" || host == "my_second_host";
return isValidHost;
);
如果你有更多的主机,你可以在那里添加一个新的检查。
【讨论】:
这应该是最好的答案。这里的许多其他答案都是四处走动,并不安全。顺便说一句,对于套接字,如何添加 badCertificateCallback? 如果我有 2 台主机,一台证书已过期,另一台证书有效,我想检查证书是否已过期,然后重定向到第二台? 不幸的是我不知道该怎么做。 AFAIK 最好的方法是验证主机或在无效时请求新证书。【参考方案7】:import 'package:http/io_client.dart';
import 'dart:io';
import 'package:http/http.dart';
import 'dart:async';
import 'dart:convert';
Future getAccessToken(String url) async
try
final ioc = new HttpClient();
ioc.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
final http = new IOClient(ioc);
http.post('url', body: "email": "xyz@xyz.com", "password": "1234").then(
(response)
print("Reponse status : $response.statusCode");
print("Response body : $response.body");
var myresponse = jsonDecode(response.body);
String token = myresponse["token"];
);
catch (e)
print(e.toString());
【讨论】:
工作。谢谢你【参考方案8】:这个问题发生在我们身上,因为我们没有使用在 nginx 上使用 let's encrypt 生成的 fullchain.pem。更改后即可解决此问题。
server
listen 443 ssl;
ssl_certificate /var/www/letsencrypt/fullchain.pem;
对于 Apache,您可能需要配置 SSLCertificateChainFile
。更多关于该问题的讨论https://github.com/flutter/flutter/issues/50699
【讨论】:
伙计们,这就是解决方案(它适用于我的 nginx)。不允许不良证书!他们很糟糕。你正在危及你的用户!系统必须能够检查 CA。【参考方案9】:在我的本地服务器上使用Dio package 请求自签名证书,我更喜欢允许特定主机而不是所有域。
//import 'package:get/get.dart' hide Response; //<-- if you use get package
import 'package:dio/dio.dart';
void main()
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
class MyHttpOverrides extends HttpOverrides
@override
HttpClient createHttpClient(SecurityContext context)
return super.createHttpClient(context)
..badCertificateCallback = ((X509Certificate cert, String host, int port)
final isValidHost = ["192.168.1.67"].contains(host); // <-- allow only hosts in array
return isValidHost;
);
// more example: https://github.com/flutterchina/dio/tree/master/example
void getHttp() async
Dio dio = new Dio();
Response response;
response = await dio.get("https://192.168.1.67");
print(response.data);
【讨论】:
createHttpClient 不是 'HttpOverrides.createHttpClient' 的有效覆盖【参考方案10】:对于那些只需要忽略某些调用的证书错误的人,您可以使用许多答案已经提到的HttpOverrides
解决方案。
但是,没有必要在全局范围内使用它。您可以通过将调用包装在 HttpOverrides.runWithHttpOverrides()
中,将其仅用于您知道会遇到握手错误的某些调用。
class IgnoreCertificateErrorOverrides extends HttpOverrides
@override
HttpClient createHttpClient(SecurityContext context)
return super.createHttpClient(context)
..badCertificateCallback = ((X509Certificate cert, String host, int port)
return true;
);
Future<void> myNonSecurityCriticalApiCall() async
await HttpOverrides.runWithHttpOverrides(() async
String url = 'https://api.example.com/non/security/critical/service';
Response response = await get(url);
// ... do something with the response ...
, IgnoreCertificateErrorOverrides());
在我的情况下,它是一个外部 API,它确实具有有效的 SSL 证书并且可以在浏览器中运行,但由于某种原因无法在我的 Flutter 应用中运行。
【讨论】:
【参考方案11】:在设备设置中检查设备日期和时间。设备日期和时间设置为之前的日期。
【讨论】:
在从其他答案复制代码之前,您应该首先检查的一件事。【参考方案12】:对于所有登陆这里的人都需要解决问题,而不仅仅是绕过它允许一切。
对我来说,在服务器端解决了问题(应该如此),代码没有变化。现在一切都有效。在所有其他解决方案中,问题仍然存在(例如,邮递员运行但它在响应状态旁边的地球上显示配置错误) 配置是 Centos/Apache/LetsEncrypt/Python3.8/Django3.1.5/Mod_wsgi/ 但我猜该解决方案对大多数 Apache/LetsEncrypt 安装都有效
解决的步骤是
-
在您要配置的虚拟主机上找到“SSLCACertificateFile”行。例如:
SSLCACertificateFile /etc/httpd/conf/ssl.crt/my_ca.crt
-
下载
https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.txt
在 /etc/httpd/conf/ssl.crt/my_ca.crt 的末尾(在 -----END CERTIFICATE----- 之后)开始一个新行并从lets-encrypt-r3-cross-signed 粘贴.txt 以下所有内容 -----BEGIN CERTIFICATE-----(包括 -----BEGIN CERTIFICATE-----)
保存/etc/httpd/conf/ssl.crt/my_ca.crt
重启 Apache httpd
参考资料: https://access.redhat.com/solutions/43575 https://letsencrypt.org/certs
您也可以在https://www.digicert.com/help/ 中检查您的证书的有效性。
【讨论】:
【参考方案13】:这是用于 http 库方法。为了在项目中全局启用此选项,您需要执行以下操作。
class MyHttpoverrides extends HttpOverrides
@override
HttpClient createHttpClient(SecurityContext context)
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=>true;
//void main() => runApp(MyApp());
void main()
HttpOverrides.global=new MyHttpoverrides();
runApp(MyApp());
更多详情:https://fluttercorner.com/certificate-verify-failed-unable-to-get-local-issuer-certificate-in-flutter/
【讨论】:
【参考方案14】:我特别需要使用 lib/client.dart
Client
接口进行 http 调用(即 http.Client 而不是 HttpClient)。这是ChopperClient
(link) 要求的。
所以我无法将 HttpClient
从 lib/_http/http.dart
直接传递给 Chopper。
ChopperClient 可以在 ioclient.IOClient
包裹的构造函数中接收 HttpClient
。
HttpClient webHttpClient = new HttpClient();
webHttpClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
dynamic ioClient = new ioclient.IOClient(webHttpClient);
final chopper = ChopperClient(
baseUrl: "https://example.com",
client: ioClient,
services: [
MfService.create()
],
converter: JsonConverter(),
);
final mfService = MfService.create(chopper);
这样您可以暂时忽略调用中的 CERTIFICATE_VERIFY_FAILED 错误。请记住 - 这仅用于开发目的。不要在生产环境中使用它!
【讨论】:
如何在生产环境中解决这个问题?? @kosiara - Bartosz Kosarzycki。我正在尝试实施您的解决方案,但我认为使用完整代码会更好。 ioclientIOClient(..) 和 MfService 看不到它们的定义位置。谢谢【参考方案15】:2021 年 1 月 30 日更新: 我知道原因,因为nginx配置了一些flutter不支持的加密算法! ,具体需要试试。
使用tls 1.3
请求网址,没问题。
例子
import 'dart:io';
main() async
HttpClient client = new HttpClient();
// tls 1.2 error
// var request = await client.getUrl(Uri.parse('https://shop.io.mi-img.com/app/shop/img?id=shop_88f929c5731967cbc8339cfae1f5f0ec.jpeg'));
// tls 1.3 normal
var request = await client.getUrl(Uri.parse('https://ae01.alicdn.com/kf/Ud7cd28ffdf6e475c8dc382380d5d1976o.jpg'));
var response = await request.close();
print(response.headers);
client.close(force: true);
【讨论】:
【参考方案16】:实际上,在我更新我的电脑上的日期和时间后,它修复了我。可能会帮助我猜的人
【讨论】:
【参考方案17】:对我来说,这是因为我使用的是 HTTPS,而 API 使用的是 HTTP,所以我只是将其更改为 HTTP 并且它可以工作。
【讨论】:
【参考方案18】:好吧,我发现问题的真正根源是我的测试设备上的时钟不同步...
【讨论】:
【参考方案19】:这个解决方案终于奏效了。感谢米拉德
final ioc = new HttpClient();
ioc.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
final http = new IOClient(ioc);
http.post(); //Your Get or Post Request
【讨论】:
【参考方案20】:对我来说,这是安卓模拟器的问题。
I just created a new android emulator that fixed my problem.
【讨论】:
【参考方案21】:我通过生成 full_chain.crt 文件解决了这个问题。
您可能已收到 your_domain.crt 文件和 your_domain.ca-bundle 文件。现在你要做的就是将crt文件和ca-bundle文件结合起来生成crt文件。
cat domain.crt domain.ca-bundle >> your_domain_full_chain.crt
然后你只需要把your_domain_full_chain.crt文件放到nginx里面就可以正常工作了。
【讨论】:
【参考方案22】:通常当您无法使用外部软件包时会发生此问题,并且当您的 PC 中有严格的防火墙设置时会发生此问题。大多数时候,当您使用公司的 PC 时,他们会安装特殊程序来阻止这些服务。所以,禁用它们,希望它会起作用。
【讨论】:
以上是关于如何在执行 POST 请求时解决颤振 CERTIFICATE_VERIFY_FAILED 错误?的主要内容,如果未能解决你的问题,请参考以下文章
在使用 Hive 进行颤振时,您如何将自定义对象从 POST 响应正文添加到框?
使用颤振客户端向 node.js express api 发出 post 请求