如何在执行 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 中的安全性。如果您有充分的理由这样做(在技术上无效的开发盒上的自签名证书),那很好。但是在将这样的代码发送到生产环境时要小心...... 不要忘记使用 HttpClient createHttpClient(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) 要求的。

所以我无法将 HttpClientlib/_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 请求

如何解决flutter中的http错误代码400缺少参数?

如何使用 POST 方法在颤振中获得 json 响应?

向Tomcat发送POST请求时如何解决浏览器中的Javascript fetch API错误?

颤振 http 标头