본문 바로가기

개발하자

POST 요청을 수행하는 동안 플러터 인증서_VERIFIY_FAILED 오류를 해결하는 방법은 무엇입니까?

반응형

POST 요청을 수행하는 동안 플러터 인증서_VERIFIY_FAILED 오류를 해결하는 방법은 무엇입니까?

나는 다트에서 포스트 요청을 보냅니다. 포스트맨과 같은 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)



이전에 이 질문을 받았을 때는 답변할 문서와 개발자가 충분하지 않았습니다. 다음 답변이 이 답변보다 더 도움이 될 수 있습니다. , &

이 세 가지 답변에서 해결책을 찾지 못한 경우 아래의 해결책을 사용해 보십시오.

제가 알기로는 모든 인증서를 허용하는 것이 올바른(그러나 나쁜) 방법입니다.

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



나는 특히 http 호출을 위한 인터페이스를 사용할 필요가 있었다.HttpClient 대신 Client). 이것은 ()에 의해 요구되었다.

그래서 나는 초퍼로 바로 갈 수 없었다. ChopperClient는 로 포장된 생성자에서 수신할 수 있습니다.

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_VERIFIY_FAILED 오류를 무시할 수 있습니다. 기억하세요 - 그것은 개발 목적으로만 사용됩니다. 프로덕션 환경에서는 이 제품을 사용하지 마십시오!




2021년 1월 30일 업데이트: nginx가 일부 암호화 알고리즘으로 구성되어 있기 때문에 그 이유를 알고 있습니다! , 구체적으로 시도할 필요가 있습니다.

요청 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 호스트인 경우 이 호스트에서만 인증서를 허용할 수 있습니다:

    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. 주 기능에서 함수 정의 뒤에 다음 줄을 추가합니다:

HttpOverrides.global = MyHttpOverrides();

코멘트는 이 문제를 통과하는 데 매우 도움이 되었습니다,

이것은 개발 모드에 있을 때 사용되어야 하며, 프로덕션에 릴리스하고 싶을 때 이 작업을 수행해야 합니다. 이 답변의 목적은 다음과 같습니다 개발을 조금 더 쉽게 하고, 생산을 위해서는 인증서 문제를 해결하고 적절하게 사용해야 하며, 사례에 도움이 될 수 있으므로 이에 대한 다른 답변을 보십시오.




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/Python3.8/Django3.1.5/Mod_wsgi/이지만 대부분의 Apache/LetsEncrypt 설치에 적합한 솔루션인 것 같습니다

해결할 단계는 다음과 같습니다

  1. 구성할 가상 호스트에서 "SSLC 인증서 파일" 행을 찾습니다. 예:

SSLCAC 인증서 파일 /etc/httpd/conf/ssl.crt/my_ca.crt

  1. 다운로드 /etc/httpd/conf/ssl.crt/my_ca.crt(------END CERTIFICATE----- 이후)의 끝에서 새 줄을 시작하고 lets-encrypt-r3-cross-signed에서 붙여넣습니다.txt 아래의 모든 것 ---------------------------------------------------------------------------------------------------
  2. /etc/httpd/conf/ssl.crt/my_ca.crt 저장
  3. Apache httpd 재시작

참조:

또한 인증서의 유효성을 확인할 수 있습니다.




이 코드는 나에게 적용된다

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에서 암호화를 사용하여 생성된 pem. 일단 변경되면 이 문제를 해결합니다.

server {
    listen 443 ssl;

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

Apache의 경우 를 구성해야 할 수 있습니다. 문제에 대한 자세한 설명




자체 서명된 인증서가 있는 로컬 서버에서 요청을 위해 사용하는 경우, 모든 도메인보다 특정 호스트를 허용하는 것을 선호합니다.

//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로 바꿨는데 작동이 되네요.




특정 통화에 대해서만 인증서 오류를 무시해야 하는 사용자의 경우 이미 많은 답변에서 언급한 솔루션을 사용할 수 있습니다.

하지만, 그것을 전세계적으로 사용할 필요는 없다. 통화를 로 포장하여 핸드셰이크 오류가 발생하는 특정 통화에만 사용할 수 있습니다.

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

나의 경우, 그것은 유효한 SSL 인증서를 가지고 있고 브라우저에서 작동하지만 어떤 이유로 내 Fluter 앱에서는 작동하지 않는 외부 API이다.




장치 설정에서 장치 날짜 및 시간을 확인합니다. 장치 날짜 및 시간이 이전 날짜로 설정됩니다.




사실 제 경우에는 제 PC의 날짜와 시간을 업데이트한 후에 수정했습니다. 누군가를 도울 수 있을 것 같아요




음, 문제의 실제 원인은 테스트 장치의 동기화 시간이 맞지 않는 것이라는 것을 알아냈어요...




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

자세한 내용은 다음을 참조하십시오:




  1. 인증서 다운로드 위치

  2. 이 파일을 assets/ca/Flash 프로젝트 루트 디렉토리에 추가합니다

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

기본 체인과 함께 작동하므로 서버에서 변경할 필요가 없습니다. 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 파일을 생성하여 문제를 해결했습니다.

귀하의_domain.crt 파일과 귀하의_domain.ca-bundle 파일을 받았을 수 있습니다. 이제 crt 파일과 ca-bundle 파일을 결합하여 crt 파일을 생성해야 합니다.

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

그러면 당신은 nginx에 당신의_domain_full_chain.crt 파일을 넣기만 하면 정상적으로 작동할 것이다.




안드로이드 에뮬레이터를 사용하면 인터넷 연결이 없을 때 이런 현상이 발생한다는 것을 알게 되었습니다. 에뮬레이터가 네트워크에 연결되어 있는지 확인합니다!




나의 경우 백엔드의 ssl 인증서를 다시 만들어야 했다. 나는 mkcert를 사용하여 인증서를 생성했고 새로운 인증서를 만들어야 했다.




로컬 SSL에 연결하는 것 외에 이 오류가 발생하는 경우 올바르게 수정하고 보안 위험이 있으므로 사용하지 마십시오!

그러나 자체 서명된 인증서를 실행하는 로컬 백엔드에 연결하려는 경우 다음을 수행할 수 있습니다.

이 클라이언트를 사용하여 로컬 백엔드가 아닌 다른 소스에 연결할 수 있습니다.

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

이 클라이언트를 사용하여 로컬 백엔드에 연결할 수 있습니다. 앱을 실행할 때 플래그를 설정해야 합니다.

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에 연결할 때만 개발 환경에 있을 때 인증서 메시지가 표시되지 않습니다. 다른 요청은 그대로 유지되며 실패할 경우 다른 방법으로 해결해야 합니다.




에뮬레이터를 사용하는 경우. 따라서 날짜와 시간이 올바른지 확인하십시오. 왜냐하면 제 경우에 저는 그 문제를 발견했기 때문입니다.


반응형