执行 POST 请求时如何解决 Flutter CERTIFICATE_VERIFY_FAILED 错误?

我在 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.example",
"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)
161859 次浏览

编辑和更新2021年2月: 当这个问题早些时候被问到,没有足够的文档和开发人员来回答。下面的答案可能比这个更有帮助: Ma‘ moon Al-Akash 的回答肯恩的回答

如果你在这3个答案中没有找到解决方案,你可以试试下面的解决方案。

返回文章页面2019年1月: 正如我发现的那样,正确的(但是不好的)方法是允许所有的证书。

HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);


String url ='xyz@xyz.example';


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);

我特别需要使用 lib/client.dart Client接口的 http 调用(即 http。客户端而不是 HttpClient)。这是 ChopperClient(链接)所要求的。

所以我不能把 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 错误。记住,这只是为了开发的目的。不要在生产环境中使用它!

2021年1月30日更新: 我知道原因,因为 nginx 配置了一些加密算法,flutter 不支持!,具体需要尝试。

使用 tls 1.3请求 URL,没问题。

例子

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);
}

最好的方法(我认为是这样)是允许为受信任的主机提供证书,因此如果您的 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;
});

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

如果你正在使用 上帝图书馆,只要这样做:

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

为了在项目中全局启用这个选项,您需要这样做:

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

Global = MyHttpOverride () ;

这个评论非常有帮助,通过这个问题,并请注意..。

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

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.example", "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());
}
}

对于每个降落在这里的人来说,需要解决的问题,而不是只是绕过它,允许一切。

对我来说,这个问题是在服务器端解决的(应该是这样) ,代码没有任何变化。现在一切都有效了。在所有其他解决方案中,这个问题仍然存在(例如,邮递员运行,但它在响应状态旁边的地球仪上显示一个配置错误) 配置是 Centos/Apache/LetsEncrypt/Python 3.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-crypt-r3-cross-signd.txt 粘贴下面的所有内容——-开始证书——-(包括——-开始证书—— -)
  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/中检查你的证书的有效性。

这个密码对我有用

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());
}

我想你也一样。

这个问题发生在我们身上,因为我们没有使用在 nginx 上使用 let’s crypt 生成的 fullchain.pem。一旦改变,它修复了这个问题。

server {
listen 443 ssl;


ssl_certificate /var/www/letsencrypt/fullchain.pem;

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

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

//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);
}

对我来说,这是因为我使用 HTTPS 和 API 使用 HTTP,所以我只是把它改为 HTTP 和它的工作。

对于那些仅在某些调用中需要忽略证书错误的人,您可以使用许多答案中已经提到的 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 应用程序中工作。

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

事实上,在我的情况下,我修复后更新的日期和时间在我的电脑。也许能帮到别人

我发现问题的根源在于测试设备的时间不同步。

这是针对 http 库方法的。为了在项目中全局启用此选项,您需要执行以下操作。 enter image description here

    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/

  1. 从 < a href = “ https://letscrypt.org/certs/lets-crypt-r3.pem”rel = “ norefrer”> https://letsencrypt.org/certs/lets-encrypt-r3.pem 下载 cert

  2. 将此文件添加到 asset/ca/Flutter 项目根目录

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

  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());
    }
    

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

这个方法终于奏效了,多亏了米拉德

    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

对我来说,这是机器人模拟器的问题。

I just created a new android emulator that fixed my problem.

我通过生成 full _ chain. crt 文件修复了这个问题。

您可能已经收到了 your _ domain。Crt 文件和你的 _ domain。Ca-bundle 文件。现在您要做的是组合 crt 文件和 ca-bundle 文件来生成 crt 文件。

cat domain.crt domain.ca-bundle >> your_domain_full_chain.crt

然后,您只需将 your _ domain _ full _ chain. crt 文件放到 nginx 中,它就会正常工作。

如果你使用 Android 模拟器,我发现这种情况发生在没有互联网连接的时候。检查您的模拟器是否有网络连接!

在我的情况下,我需要重新制作后端的 ssl 证书。我已经使用 mkcert 生成了证书,只需要生成新的证书。

注意: 如果这个错误发生在尝试连接到本地 SSL 之外,请正确地修复它,不要仅仅使用 badCertificateCallback = (cert, host, port) => true,因为它是一个安全风险!

但是。.如果因为要连接到运行自签名证书的本地后端而遇到此问题,则可以执行下列操作。

您可以使用此客户端连接到本地后端之外的其他源。

class AppHttpClient {
AppHttpClient({
Dio? client,
}) : client = client ?? Dio() {
if (kDebugMode) {
// this.client.interceptors.add(PrettyDioLogger());
}
}
final Dio client;
}

您可以使用此客户端连接到本地后端。确保在运行应用程序时设置了 --dart-define FLAVOR=development标志。

class InternalApiHttpClient extends AppHttpClient {
ApiHttpClient({
super.client,
required this.baseUrl,
}) {
_allowBadDevelopmentCertificate();
}


void _allowBadDevelopmentCertificate() {
const flavor = String.fromEnvironment('FLAVOR');
if (flavor == 'development') {
final httpClientAdapter =
super.client.httpClientAdapter as DefaultHttpClientAdapter;
httpClientAdapter.onHttpClientCreate = (client) {
return client..badCertificateCallback = (cert, host, port) => true;
};
}
}


final Uri baseUrl;
}

只有在连接到本地 API 时,才会在开发环境中取消证书消息。任何其他请求保持不变,如果失败应该用另一种方式解决。