如何解决执行POST请求时出现的CERTIFICATE_VERIFY_FAILED错误?

Adi*_*ood 8 dart flutter

我正在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))
Run Code Online (Sandbox Code Playgroud)

这是我的函数代码-

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());
    }
Run Code Online (Sandbox Code Playgroud)

这是完整的错误正文-

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)
Run Code Online (Sandbox Code Playgroud)

Smi*_*ith 97

  1. 从https://letsencrypt.org/certs/lets-encrypt-r3.pem下载证书

  2. 将这个文件添加到assets/ca/ Flutter项目根目录下

  3. 在pubspec.yaml中添加assets/ca/assets目录

  4. 在您的应用程序初始化中添加以下代码:

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
      SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
    
      runApp(MyApp());
    }
    
    Run Code Online (Sandbox Code Playgroud)

它与默认链一起使用,因此不需要在服务器上进行任何更改。Android < 7.1.1 客户端仍可以在浏览器上下文中进行访问。

  • 导入'包:flutter/services.dart'; 对于 PlatformAssetBundle 并导入 'dart:io'; 对于安全上下文 (2认同)
  • 这对于安全目的有好处吗?由于我们使用的是公共证书 (2认同)

Ma'*_*ash 80

只是为了清楚起见,特别是为了新来者 flutter/dart,为了在项目中全局启用此选项,您需要执行以下操作:

  1. 在 main.dart 文件中,添加或导入以下类:
 class MyHttpOverrides extends HttpOverrides{
  @override
  HttpClient createHttpClient(SecurityContext context){
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
  }
}
Run Code Online (Sandbox Code Playgroud)
  1. 在您的主函数中,在函数定义后添加以下行:

HttpOverrides.global = new MyHttpOverrides();

这个评论对通过这个事情很有帮助,请注意......

这应该在开发模式下使用,当你想要发布到生产时要这样做,这个答案的目的是 让你的开发更容易一些,对于生产,你需要修复你的证书问题并使用它正确地,请查看其他答案,因为它可能对您的情况有所帮助。

  • 不要忘记使用 HttpClient `createHttpClient(SecurityContext? context)` (注意问号)来实现 null 安全 (4认同)
  • 对于空安全用户添加 ? 在 SecurityContext 之后,如 `SecurityContext?` (3认同)
  • 这帮助我使用了一个无法访问内部 http 客户端实例的第三方库。现在可以了,谢谢 (2认同)
  • 这个解决方案效果很好。就我而言,这是典型的场景:开发盒上的自签名证书。 (2认同)
  • 这会起作用,但它会有效地禁用 BoringSSL 中的安全性。如果您有充分的理由这样做(开发盒上的自签名证书在技术上无效),那就很好。但在将这样的代码传送到生产环境时要小心...... (2认同)

Hos*_*our 19

如果您使用的是Dio 库,请执行以下操作:

Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    (HttpClient client) {
  client.badCertificateCallback =
      (X509Certificate cert, String host, int port) => true;
  return client;
};
Run Code Online (Sandbox Code Playgroud)

  • 这是一个可行的解决方案,对于遇到 @Zorro 提到的错误的任何人,您需要将 `package:dio/adapter.dart` 添加到您的导入中。 (4认同)
  • 我得到:``名称'DefaultHttpClientAdapter'不是一种类型,因此它不能在'as'表达式中使用。`` (3认同)

小智 14

检查设备设置中的设备日期和时间。设备日期和时间设置为之前的日期。

  • 在从其他答案复制代码之前,您应该首先检查的一件事。 (4认同)

Adi*_*ood 13

我发现,正确的方法(但很糟糕)是允许所有证书。

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);
Run Code Online (Sandbox Code Playgroud)

  • 大约一年后是否有机会找到更好的解决方案? (12认同)

Ped*_*ngo 9

最好的方法(我认为)是允许可信主机的证书,所以如果你的 API 主机是"api.my_app"你只能允许来自这个主机的证书:

HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) {
 final isValidHost = host == "api.my_app";
 return isValidHost;
});
Run Code Online (Sandbox Code Playgroud)

如果你有更多的主机,你可以在那里添加一个新的检查。


Mil*_*adi 9

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());
      }
    }
Run Code Online (Sandbox Code Playgroud)


Gra*_*ign 9

此代码对我有用

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());
}
Run Code Online (Sandbox Code Playgroud)

我想这对你来说也是一样的......

  • 这适用于移动设备,但不适用于 flutter web (2认同)

小智 8

这是针对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());
}
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息:https://fluttercorner.com/certificate-verify-failed-unable-to-get-local-issuer-certificate-in-flutter/


Zor*_*rro 7

使用Dio 包在我的本地服务器上使用自签名证书请求,我更喜欢允许特定主机而不是所有域。

//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);
}
Run Code Online (Sandbox Code Playgroud)

  • createHttpClient 不是“HttpOverrides.createHttpClient”的有效覆盖 (4认同)

Joh*_*ton 6

对于每个登陆这里并需要解决问题而不是仅仅绕过它允许一切的人来说。

对我来说,问题在服务器端解决了(应该如此),代码没有任何变化。现在一切都有效了。在所有其他解决方案上,问题仍然存在(例如,邮递员运行,但它在响应状态旁边的地球上显示配置错误)配置是 Centos/Apache/LetsEncrypt/Python3.8/Django3.1.5/Mod_wsgi/ 但我猜该解决方案对于大多数 Apache/LetsEncrypt 安装都有效

解决步骤是

  1. 在您要配置的虚拟主机上找到“SSLCACertificateFile”行。例如:

SSLCACertificateFile /etc/httpd/conf/ssl.crt/my_ca.crt

  1. 下载 https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.txt 在 /etc/httpd/conf/ssl.crt/my_ca.crt 末尾(在 -----END CERTIFICATE 之后-----) 开始一个新行并从 let-encrypt-r3-cross-signed.txt 粘贴以下所有内容 -----BEGIN CERTIFICATE----- (包括 -----BEGIN CERTIFICATE--- --)
  2. 保存/etc/httpd/conf/ssl.crt/my_ca.crt
  3. 重新启动 Apache httpd

参考资料: https://access.redhat.com/solutions/43575 https://letsencrypt.org/certs

您还可以在https://www.digicert.com/help/中检查证书的有效性。


ken*_*ken 6

这个问题发生在我们身上,因为我们没有使用在 nginx 上使用 let's encrypt 生成的 fullchain.pem。一旦更改它就解决了这个问题。

server {
    listen 443 ssl;

    ssl_certificate /var/www/letsencrypt/fullchain.pem;
Run Code Online (Sandbox Code Playgroud)

对于 Apache,您可能需要配置SSLCertificateChainFile. 关于该问题的更多讨论https://github.com/flutter/flutter/issues/50699


小智 6

对我来说,这是因为我使用的是 HTTPS 而 API 使用 HTTP,所以我只是将其更改为 HTTP 并且它可以工作。


Mag*_*s W 6

对于那些只需要忽略某些调用的证书错误的人,您可以使用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(Uri.parse(url));

    // ... do something with the response ...
  }, IgnoreCertificateErrorOverrides());
}

Run Code Online (Sandbox Code Playgroud)

就我而言,它是一个外部 API,它具有有效的 SSL 证书并且可以在浏览器中运行,但由于某种原因无法在我的 Flutter 应用程序中运行。


小智 5

对我来说,这是android模拟器的问题。我刚刚创建了一个新的 Android 模拟器来解决我的问题。


小智 5

如果您使用 Dio 库,则“DefaultHttpClientAdapter”已被弃用,不应使用。请改用 IOHttpClientAdapter。

首先手动导入:

import 'package:dio/io.dart';
Run Code Online (Sandbox Code Playgroud)

然后使用“IOHttpClientAdapter”而不是“DefaultHttpClientAdapter”

(_dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate =
      (HttpClient client) {
    client.badCertificateCallback =
        (X509Certificate cert, String host, int port) => true;
    return client;
  };
Run Code Online (Sandbox Code Playgroud)