Android报错:CLEARTEXT通信未被网络安全策略允许问题排查
解决Retrofit连接TLS 1.2 API时的“CLEARTEXT communication”错误
一、错误的非HTTP诱因
- BASE_URL协议错误:即使你认为修改过地址,仍需确认
BASE_URL是否严格以https://开头,拼写错误或环境变量替换失败可能导致实际请求走HTTP。 - 自定义OkHttpClient未生效:如果
getTrustManagerFactory返回null,client会为null,Retrofit将使用默认客户端,若默认客户端无法处理证书信任,可能间接触发CLEARTEXT错误(比如请求被降级)。 - 服务器重定向到HTTP:API服务器可能将HTTPS请求重定向至HTTP地址,OkHttp默认跟随重定向,此时会触发安全策略限制。
- TLS版本未强制锁定:仅指定
SSLContext.getInstance("TLS")可能包含TLS 1.0/1.1,而服务器仅支持1.2,导致握手失败后请求被降级为HTTP。
二、遗漏的配置检查与修复
1. 确认BASE_URL的协议合法性
确保BASE_URL无拼写错误,例如:
private const val BASE_URL = "https://your-api-domain.com/"
可在create方法中打印BASE_URL的值,验证实际使用的地址。
2. 确保自定义OkHttpClient被正确使用
避免client为null导致Retrofall使用默认客户端,修改代码添加异常抛出:
val client = sslsocket?.let { OkHttpClient.Builder() .sslSocketFactory(it, tmf?.trustManagers?.get(0) as X509TrustManager) .hostnameVerifier(hostnameVerifier) .build() } ?: throw IllegalStateException("Failed to initialize SSL OkHttpClient")
3. 强制锁定TLS 1.2版本
修改getSSLSocketFactory方法,确保SSLSocket仅启用TLS 1.2:
private fun getSSLSocketFactory(tmf: TrustManagerFactory): SSLSocketFactory { val sslContext: SSLContext = SSLContext.getInstance("TLSv1.2") sslContext.init(null, tmf.trustManagers, null) return object : SSLSocketFactory() { private val delegate = sslContext.socketFactory override fun getDefaultCipherSuites(): Array<String> = delegate.defaultCipherSuites override fun getSupportedCipherSuites(): Array<String> = delegate.supportedCipherSuites override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket { val socket = delegate.createSocket(s, host, port, autoClose) as SSLSocket socket.enabledProtocols = arrayOf("TLSv1.2") return socket } override fun createSocket(host: String, port: Int): Socket { val socket = delegate.createSocket(host, port) as SSLSocket socket.enabledProtocols = arrayOf("TLSv1.2") return socket } override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket { val socket = delegate.createSocket(host, port, localHost, localPort) as SSLSocket socket.enabledProtocols = arrayOf("TLSv1.2") return socket } override fun createSocket(host: InetAddress, port: Int): Socket { val socket = delegate.createSocket(host, port) as SSLSocket socket.enabledProtocols = arrayOf("TLSv1.2") return socket } override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket { val socket = delegate.createSocket(address, port, localAddress, localPort) as SSLSocket socket.enabledProtocols = arrayOf("TLSv1.2") return socket } } }
4. 配置网络安全策略文件
在res/xml/network_security_config.xml中添加自定义证书信任配置:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">你的API域名</domain> <trust-anchors> <certificates src="@raw/fullchain2" /> <certificates src="system" /> </trust-anchors> </domain-config> </network-security-config>
并在AndroidManifest.xml的application标签中引用:
<application ... android:networkSecurityConfig="@xml/network_security_config">
5. 拦截重定向请求
添加拦截器检查重定向地址是否为HTTPS,避免被降级:
val client = sslsocket?.let { OkHttpClient.Builder() .sslSocketFactory(it, tmf?.trustManagers?.get(0) as X509TrustManager) .hostnameVerifier(hostnameVerifier) .addInterceptor { chain -> val response = chain.proceed(chain.request()) val redirectUrl = response.request.url.toString() if (!redirectUrl.startsWith("https://")) { throw IOException("Redirect to non-HTTPS URL: $redirectUrl") } response } .build() } ?: throw IllegalStateException("Failed to initialize SSL OkHttpClient")
三、排查步骤
- 打印
BASE_URL确认协议为HTTPS; - 打印
client对象,确认自定义客户端已被创建; - 用抓包工具(如Charles)查看请求实际地址,检查是否存在HTTP重定向;
- 验证
fullchain2.pem是否包含完整证书链,避免中间证书缺失导致信任失败。
内容的提问来源于stack exchange,提问作者KyoungMin Yu
相关产品推荐
相关产品推荐

