前言
随着网络技术的发展, 越来越多的公司开始使用https作为网络请求协议, 但是身为这个时代的开发者, 却很少有人了解其中的原理, 每次调接口的时候都是浪费大量的时间来对接, 联调, 排查错误, 经常会说的一句, 接口怎么不通, 而这个地方的资料虽然产量高但每一篇都很重量级, 导致了开发者学习困难, 所以这篇文章产生了, 我会用解决问题的方式来推进文章, 由浅入深, 依次讲解, 下面就跟着我们的文章一起来看吧.
一.概念
百度上一大堆, 想看概念的可以先查一下, 这里使用普通话来叙述.
http:
全名: Hyper Text Transfer Protocol
描述: 常用的网络协议
https:
全名: Hyper Text Transfer Protocol over Secure Socket Layer
描述: 常用的网络协议, 比http更安全
1.https安全在什么地方呢?
下面就要引入一个概念ssl, 全名(Secure Sockets Layer 安全套接层), 它是一种安全协议, 协议原理是在传输层对请求信息进行了加密, 这样别人就不太会轻易看到你发送的信息了.
https = http + ssl
2.ssl证书
读了上文已经知道了, ssl就是https协议的加密核心, 那我要怎么使用它呢,
下面就要介绍主角ssl证书了, ssl证书的种类有两种, 一种是自建证书, 一种是CA证书, 自建证书顾名思义就是自己制作出来的, CA证书则是从正规的证书颁发机构花钱买的或免费申请的, 网络请求过程大概是这样的, 发送请求的时候使用证书里面的秘钥对请求信息做加密处理, 到达客户端再用秘钥进行解密, 这两个步骤都是系统帮你自动完成的, 因为存在这个过程所以https
通常来讲请求耗时更长, 不过为了安全, 这些牺牲还是可以接受的.
二.快速开始
我会从前台和后台两个角度依次讲解, 先说一下iOS
端如何正常访问https
接口, 再来说一下JAVA
后台是怎么配置证书来实现https
访问的.
1.iOS配置https
CA证书:
上面已经说过了, ssl证书一共分为两种, 自建证书和CA证书, 那么对于我们来说CA证书自然是最省事的, 可以用一句话概括, 跟正常网络请求没有任何区别.
甚至你连这个都不用改动, YES和NO, 均可以访问通过.(亲测)
下面上AF代码 , 正常的不能再正常了 - -
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:url parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@", error);
}];
所以公司使用CA证书你就偷着乐去吧, 对开发完全没影响.
自建证书:
这个比起CA证书就要麻烦很多了, 不过只要学会了方法还是很好解决的.
你通常会遇到的错误是这个
错误1
Task <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1> load failed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://www.objcat.com:8082/hello, NSErrorFailingURLKey=https://www.objcat.com:8082/hello, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1>, NSLocalizedDescription=cancelled} [-999]
2019-01-09 15:37:31.069920+0800 https网络测试[23697:794090] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://www.objcat.com:8082/hello, NSErrorFailingURLKey=https://www.objcat.com:8082/hello, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1>, NSLocalizedDescription=cancelled}
2019-01-09 15:37:31.075854+0800 https网络测试[23697:794147] Task <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1> finished with error - code: -999
2019-01-09 15:37:31.077351+0800 https网络测试[23697:794148] Task <6A6DB526-EC21-4E85-BACC-1415D23C3057>.<1> HTTP load failed (error code: -999 [1:89])
还是这个
错误2
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
"<cert(0x7f86d2017400) s: Andy i: Andy>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://www.objcat.com:8082/hello, NSErrorFailingURLStringKey=https://www.objcat.com:8082/hello, NSUnderlyingError=0x600001f21500 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x60000230f2a0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x7f86d2017400) s: Andy i: Andy>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <F8DB0C28-4CD3-417B-B89B-B0DCCABB6EA6>.<1>"
), _kCFStreamErrorCodeKey=-9802, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <F8DB0C28-4CD3-417B-B89B-B0DCCABB6EA6>.<1>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x60000230f2a0>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}
或是这个
错误3
*** Terminating app due to uncaught exception 'Invalid Security Policy', reason: 'A security policy configured with `AFSSLPinningModeCertificate` can only be applied on a manager with a secure base URL (i.e. https)'
*** First throw call stack:
(
0 CoreFoundation 0x000000010b27029b __exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010a80c735 objc_exception_throw + 48
2 https网络测试 0x0000000109426325 -[AFHTTPSessionManager setSecurityPolicy:] + 613
3 https网络测试 0x000000010942276c -[ViewController viewDidLoad] + 172
4 UIKitCore 0x000000010f379781 -[UIViewController loadViewIfRequired] + 1186
5 UIKitCore 0x000000010f379be0 -[UIViewContro
先把下巴合上, 口水擦一擦, 我们下面就来说一下这几个错误都是什么, 该怎么解决.
错误1 解决方案:
这里提供一个最好的解法, 也是最简单的, 可以用一个成语来形容 - 敞门入场, 我什么都不验证, 只想拿到我的数据, 其他跟我没关系.
首先设置下面两项
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 是否允许无效证书, 默认为NO
manager.securityPolicy.allowInvalidCertificates = YES;
// 是否校验域名, 默认为YES
manager.securityPolicy.validatesDomainName = NO;
然后这个地方设置为YES, Allow Arbitrary Loads
允许任意加载
上述设置任何网络都可以保证通畅访问, 如果是来找答案的, 恭喜你问题已经解决了.
下面内容有可能引起不适 慎读:
错误2 解决方案
你在寻找答案的时候有可能看到另外一种解法, 就是在AF中配置证书来保持访问正常, 说实话这种方法是完全不推荐的, 在强调一遍, 让公司去买CA证书.
你可能对下面的代码很熟悉, 摘自互联网
- (AFSecurityPolicy *)customSecurityPolicy {
// 先导入证书 证书由服务端生成,具体由服务端人员操作
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"keystore" ofType:@"cer"];//证书的路径 xx.cer
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
// allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
// 如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;
//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = NO;
securityPolicy.pinnedCertificates = [[NSSet alloc] initWithObjects:cerData, nil];
return securityPolicy;
}
路人甲: 对对对, 就是这垃圾东西, 我调了好几天.
路人乙: 麻蛋的, 我写的都对, 咋访问不通呢, 是不是后台证书给错了.
解决方法也简单
1.让后台提供有效的ssl证书, 亲测p12导出cer是完全可以使用的
2.检查你的代码有没有开启 Allow Arbitrary Loads = YES
3.帮助后台对接调试
错误2就会在没有开启Allow Arbitrary Loads
的时候出现, 因为自建证书并非正规的ssl请求, 所以需要开启允许所有访问.
然而在你做完这些之后会遇到错误3
A security policy configured with `AFSSLPinningModeCertificate` can only be applied on a manager with a secure base URL (i.e. https)'
意思是使用AFSSLPinningModeCertificate
模式必须配置一个baseURL, 这个可以是你的域名或者ip, 目前还不知道有什么用, 随便配置一个即可.
AFHTTPSessionManager *manager = [[AFHTTPSessionManager manager] initWithBaseURL:[NSURL URLWithString:@"https://www.baidu.com"]];
之后如果没什么失误的话, 就可以访问接口了...
到这里告一段落.
2.Java配置https
CA证书:
首先来介绍一下CA证书:
一共分为三种
DV SSL
DV SSL证书是只验证网站域名所有权的简易型SSL证书,可10分钟快速颁发,能起到加密传输的作用,但无法向用户证明网站的真实身份。
目前市面上的免费证书都是这个类型的, 只是提供了对数据的加密, 但是对提供证书的个人和机构的身份不做验证。
例如我自己的证书:
OV SSL
OV SSL, 提供加密功能, 对申请者做严格的身份审核验证和DV SSL的区别在于, OV SSL 提供了对个人或者机构的审核, 能确认对方的身份, 安全性更高.
所以这部分的证书申请是收费的~
例如百度的证书:
EV SSL
EV最安全, 最严格, 超安EV SSL证书遵循全球统一的严格身份验证标准,是目前业界安全级别最高的顶级SSL证书, 验证过身份的公司名称可以直接显示在浏览器上, 是不是很高大上.
例如证书颁发机构的官网:
好了下面我们就来申请一个免费的证书吧!
首先去阿里云或别的机构申请CA证书, 这里举例申请一个免费的.
阿里云控制台 -> 产品与服务 -> 搜索ssl
可以看到证书很贵, 这里面有一个免费的, 但是点出来需要点技巧
之后选择支付就可以了, 支付完成会发现多了个证书
填写基本信息
然后点验证就可以了, 需要等待一些时间
------ 一段时间后 --------
我们可以看到证书审核好了, 我们就可以直接配置了, 首先把证书下载下来, 密码在同目录的文本文件里, 然后开始配置, 这里以springcloud
为例
下载后的证书是pfx
格式的, 我们先把它导入钥匙串, 然后导出为p12
文件
然后把证书放到resources
目录下, 我改了个名请忽略 - -
server:
#服务端口号
port: 8082
ssl:
key-store: classpath:haha.p12
key-store-password: 123456
key-store-type: PKCS12
之后配置这些即可, 项目会自动开启https
.
之后访问接口试试
我们可以看到证书是有效的, 这个服务器是配置在我本地的, 我们用ip来访问一下.
我们会发现, 同样的接口, 把域名换成ip就不可以访问了, 这也验证了我们DV证书是校验域名的这个原理.
我们可以直接在证书上查看我们信任的域名
自建证书:
自建证书配置也十分简单, 首先用java自带的keytool来生成一个自建证书
keytool -genkey -alias tomcat -dname "CN=objcat,OU=objcat,O=objcat,L=PuDongXinQu,ST=ShangHai,C=CN" -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
-genkey
: 制作证书固有命令
-alias
: 别名
-dname
: 填写证书基本信息
CN(Common Name 名字与姓氏)
OU(Organization Unit 组织单位名称)
O(Organization 组织名称)
L(Locality 城市或区域名称)
ST(State 省/市/自治区名称)
-storetype
: 证书保存类型
-keyalg
: 加密方式
-keysize
: 秘钥长度
-keystore
: 证书导出时的名称
-validity
: 证书有效期
然后输入证书密码就可以完成创建了!
双击证书, 输入密码就能导入钥匙串了, 我们来看看
这个证书的使用方法和CA证书一样
1.放在resources
目录下
2.填写配置文件 - 参考CA证书配置
启动服务!
访问接口
https://localhost:8082/hello
之后点击继续前往
这样虽然可以访问, 但是我们可以看到上面写的三个红色的大字, 不安全, 所以我们需要解决一下这个问题, 就是信任证书!
我们发现信任证书之后safari
是可以访问通的, 但是chrome
还是会报错不安全
Certificate - Subject Alternative Name missing
The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.
Certificate - missing
This site is missing a valid, trusted certificate (net::ERR_CERT_COMMON_NAME_INVALID).
然后我们就开始着手解决这两个问题
根据错误提示, 是因为我的证书没有做域名认证造成的, 所以我们在生成证书的时候给它添加一个域名
加上下面这句即可
-ext san=dns:www.objcat.com
完整命令
keytool -genkey -alias tomcat -dname "CN=objcat,OU=objcat,O=objcat,L=PuDongXinQu,ST=ShangHai,C=CN" -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 -ext san=dns:www.objcat.com
我们再次生成证书, 然后重新配置
之后我们来修改 www.objcat.com 映射到本机域名
127.0.0.1 www.objcat.com
然后重启服务器
这次试用域名访问接口试试
我们发现问题解决了!
网友评论