Android P默认https传输协议, 通过SSLSocketFactory手动添加了(数据交互)服务端的证书,但加载的图片资源、html等可能使用的http协议,导致资源无法访问。
先贴一下证书的添加
private static final String CLIENT_TRUST_PASSWORD = "123456";//信任证书密码
private static final String CLIENT_AGREEMENT = "SSL";//使用协议
private static final String CLIENT_TRUST_MANAGER = "X509";
private static final String CLIENT_TRUST_KEYSTORE = "BKS";
private static SSLContext sslContext ;
public static SSLContext getSslSocket(Context context) {
try {
//取得SSL的SSLContext实例
sslContext = SSLContext.getInstance(CLIENT_AGREEMENT);
//取得TrustManagerFactory的X509密钥管理器实例
TrustManagerFactory trustManager = TrustManagerFactory.getInstance(CLIENT_TRUST_MANAGER);
//取得BKS密库实例
KeyStore tks = KeyStore.getInstance(CLIENT_TRUST_KEYSTORE);
InputStream is = context.getResources().openRawResource(R.raw.xxx);
try {
tks.load(is, CLIENT_TRUST_PASSWORD.toCharArray());
} finally {
is.close();
}
//初始化密钥管理器
trustManager.init(tks);
//初始化SSLContext
sslContext.init(null, trustManager.getTrustManagers(), null);
} catch (Exception e) {
Log.e("SslContextFactory", e.getMessage());
}
return sslContext;
}
再在okhttp中引用
SSLSocketFactory sslSocketFactory = Https.getSslSocket(context).getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient()
.newBuilder()
.sslSocketFactory(sslSocketFactory)
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS);
- 问题1:sslSocketFactory(sslSocketFactory)AS提示该方法已过时;
- 问题2:系统默认通讯为https,该方法只有在发起okhttp请求时才可用。
过时不过时的,能用就先不管了,后续再找替代方法。主要的问题在于问题2
,当在请求其他资源时,如果不是https协议,就无法请求。关键点在于:
android:usesCleartextTraffic=“true”
即是否使用明文通讯
方案:动态设置android:usesCleartextTraffic的值
百度了一下,各种骚套路 Java一切皆可反射!
不过大部分例子都是动态更改包名、icon等,以及通过gradle区分debug与上线版本等。本人能力有限,这些例子都不大适用。
拿着webview上的错误,Google一波。
Android 8: Cleartext HTTP traffic not permitted
这里边的都是只信任自己的域名
,也就是添加白名单。例如:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">***Your URL(ex: 127.0.0.1)***</domain>
</domain-config>
</network-security-config>
但我需要的是反其道而行之,直接来个反相?
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">***Your URL(ex: 127.0.0.1)***</domain>
</domain-config>
</network-security-config>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true">
设置全局明文传输,只有在指定域名下才适用https。
结果很显然不行,设置方式不对。
再搜一下:How to allow all Network connection types HTTP and HTTPS in Android (9) Pie?
这一页的回答虽然不多,但看到了新的东西:base-config
。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
也就是说,里边至少有两个标签:domain-config
和base-config
查到了关键点,看看国内这方面的文章,一搜,果然有,算是一篇科普向的文章:Android网络安全配置
列出两个关键点:
- base-config,默认的配置,不在 domain-config 范围内的所有连接所使用的配置。
<trust-anchors>
证书集合,可包裹多个 证书
- domain-config。满足domain规则所使用的配置,可配置任意多个,domain-config的嵌套表示继承外层的配置规则。
<trust-anchors>
证书集合
<pin-set>
固定的证书,通过 expiration 配置过期时间,可包裹多个 证书
<domain>
域名规则,通过 includeSubdomains 配置是否支持子域名
<domain-config>
嵌套规则
依葫芦画瓢:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">地址</domain>
</domain-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
大概意思是:默认使用明文,但在与地址
通讯时,使用https协议加密传输。
运行一下,使用Fiddler抓抓包,结果如下:
数据交互是https协议,同时可以加载http的资源。
好,完结。