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;
};
프로젝트에서 이 옵션을 전체적으로 사용하려면 다음을 수행해야 합니다:
- 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();
코멘트는 이 문제를 통과하는 데 매우 도움이 되었습니다,
이것은 개발 모드에 있을 때 사용되어야 하며, 프로덕션에 릴리스하고 싶을 때 이 작업을 수행해야 합니다. 이 답변의 목적은 다음과 같습니다 개발을 조금 더 쉽게 하고, 생산을 위해서는 인증서 문제를 해결하고 적절하게 사용해야 하며, 사례에 도움이 될 수 있으므로 이에 대한 다른 답변을 보십시오.
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 설치에 적합한 솔루션인 것 같습니다
해결할 단계는 다음과 같습니다
- 구성할 가상 호스트에서 "SSLC 인증서 파일" 행을 찾습니다. 예:
SSLCAC 인증서 파일 /etc/httpd/conf/ssl.crt/my_ca.crt
- 다운로드 /etc/httpd/conf/ssl.crt/my_ca.crt(------END CERTIFICATE----- 이후)의 끝에서 새 줄을 시작하고 lets-encrypt-r3-cross-signed에서 붙여넣습니다.txt 아래의 모든 것 ---------------------------------------------------------------------------------------------------
- /etc/httpd/conf/ssl.crt/my_ca.crt 저장
- 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());
}
자세한 내용은 다음을 참조하십시오:
인증서 다운로드 위치
이 파일을 assets/ca/Flash 프로젝트 루트 디렉토리에 추가합니다
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 < 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에 연결할 때만 개발 환경에 있을 때 인증서 메시지가 표시되지 않습니다. 다른 요청은 그대로 유지되며 실패할 경우 다른 방법으로 해결해야 합니다.
에뮬레이터를 사용하는 경우. 따라서 날짜와 시간이 올바른지 확인하십시오. 왜냐하면 제 경우에 저는 그 문제를 발견했기 때문입니다.
'개발하자' 카테고리의 다른 글
파이썬의 opencv에서 "QObject::moveToThread:" 오류를 수정하는 방법은 무엇입니까? (0) | 2023.02.21 |
---|---|
Python PHP 세션 직렬화 해제 (0) | 2023.02.20 |
수평으로 스크롤할 수 있으며 스냅 효과가 있는 카드(스냅 효과 포함) (0) | 2023.02.19 |
유형 스크립트: 익스프레스 연장.자체 클래스가 있는 세션 인터페이스 (0) | 2023.02.18 |
플러터 - 요소를 제거할 때 UI가 올바르게 업데이트되지 않음 (1) | 2023.02.18 |