1. 为什么Glide加载HTTPS图片会出问题第一次在项目中使用Glide加载HTTPS链接的图片时我遇到了一个令人困惑的现象——图片加载器直接抛出了SSLHandshakeException。作为一个从Glide 3.x版本就开始使用的老开发者这种情况在HTTP时代从未出现过。经过排查发现这实际上是Android网络层与Glide协同工作时的一个典型问题。HTTPS协议要求建立安全连接时需要验证服务器证书的有效性。而Glide底层默认使用的是系统的HTTP栈在Android 4.4上是OkHttp。当服务器配置了自签名证书或者证书链不完整时系统默认的证书验证机制就会拒绝连接。这不同于HTTP时代可以直接建立连接的情况。更具体地说问题通常出现在以下几种场景测试环境使用自签名证书CDN服务商使用了非权威CA颁发的证书证书链中缺少中间证书服务器SNI配置不正确2. 基础解决方案忽略证书验证仅限测试环境在开发测试阶段我们可以通过自定义GlideModule来绕过证书验证。以下是具体实现步骤2.1 创建自定义网络组件首先需要实现Glide的ModelLoaderFactorypublic class UnsafeOkHttpClientFactory implements Factory { Override public ModelLoader build(MultiModelLoaderFactory multiFactory) { return new OkHttpUrlLoader(getUnsafeOkHttpClient()); } Override public void teardown() {} }2.2 配置不安全的OkHttpClient关键是要创建一个禁用所有SSL验证的OkHttpClient实例private static OkHttpClient getUnsafeOkHttpClient() { try { final TrustManager[] trustAllCerts new TrustManager[]{ new X509TrustManager() { Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} Override public void checkServerTrusted(X509Certificate[] chain, String authType) {} Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }; final SSLContext sslContext SSLContext.getInstance(SSL); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); return new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager)trustAllCerts[0]) .hostnameVerifier((hostname, session) - true) .build(); } catch (Exception e) { throw new RuntimeException(e); } }警告这种方法完全禁用了SSL验证会使应用面临中间人攻击风险仅适用于测试环境2.3 注册自定义组件在自定义GlideModule中注册我们的工厂GlideModule public class MyGlideModule extends AppGlideModule { Override public void registerComponents(Context context, Glide glide, Registry registry) { registry.replace(GlideUrl.class, InputStream.class, new UnsafeOkHttpClientFactory()); } }3. 生产环境解决方案正确配置证书信任对于正式发布的应用我们应该采用更安全的证书验证方式。以下是几种推荐方案3.1 使用权威CA颁发的证书最根本的解决方案是确保服务器使用受信任的CA如DigiCert、Lets Encrypt颁发的有效证书。可以通过以下命令检查证书链openssl s_client -connect yourdomain.com:443 -showcerts3.2 自定义信任管理器如果必须使用私有CA可以在客户端内置CA证书将CA证书.crt或.pem格式放入res/raw目录创建自定义TrustManagerInputStream caInput context.getResources().openRawResource(R.raw.your_ca); CertificateFactory cf CertificateFactory.getInstance(X.509); Certificate ca cf.generateCertificate(caInput); KeyStore keyStore KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry(ca, ca); TrustManagerFactory tmf TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext sslContext SSLContext.getInstance(TLS); sslContext.init(null, tmf.getTrustManagers(), null);3.3 使用CertificatePinner推荐OkHttp提供了更精细的证书锁定机制CertificatePinner certificatePinner new CertificatePinner.Builder() .add(yourdomain.com, sha256/YourCertificatePublicKeyHash) .build(); OkHttpClient client new OkHttpClient.Builder() .certificatePinner(certificatePinner) .build();可以通过以下命令获取证书的SHA256哈希openssl x509 -in certificate.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base644. 高级场景处理4.1 处理SNI问题某些CDN服务需要正确的SNIServer Name Indication配置。可以通过自定义OkHttpClient解决OkHttpClient client new OkHttpClient.Builder() .connectionSpecs(Arrays.asList( ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT )) .build();4.2 处理HTTP/2问题如果服务器仅支持HTTP/2需要明确启用OkHttpClient client new OkHttpClient.Builder() .protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1)) .build();4.3 调试技巧启用OkHttp的日志拦截器有助于排查问题HttpLoggingInterceptor logging new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient client new OkHttpClient.Builder() .addInterceptor(logging) .build();记得在release版本中移除这个拦截器。5. 性能优化建议5.1 连接池配置合理的连接池配置可以提升HTTPS连接性能OkHttpClient client new OkHttpClient.Builder() .connectionPool(new ConnectionPool(5, 10, TimeUnit.MINUTES)) .build();5.2 会话复用启用SSL会话复用可以减少握手开销OkHttpClient client new OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, trustManager) .hostnameVerifier(hostnameVerifier) .build();5.3 缓存策略配置磁盘缓存可以避免重复下载GlideModule public class MyGlideModule extends AppGlideModule { Override public void applyOptions(Context context, GlideBuilder builder) { builder.setDiskCache(new InternalCacheDiskCacheFactory(context, 100 * 1024 * 1024)); } }6. 兼容性处理6.1 Android 7的网络安全配置对于Android 7及以上版本还需要在res/xml/network_security_config.xml中配置network-security-config domain-config cleartextTrafficPermittedfalse domain includeSubdomainstrueyourdomain.com/domain trust-anchors certificates srcraw/your_ca/ /trust-anchors /domain-config /network-security-config然后在AndroidManifest.xml中引用application android:networkSecurityConfigxml/network_security_config ... 6.2 处理证书吊销对于需要检查证书吊销状态的场景X509TrustManager trustManager (X509TrustManager) trustManagers[0]; trustManager.checkServerTrusted(chain, authType); CertPathValidator validator CertPathValidator.getInstance(PKIX); PKIXParameters params new PKIXParameters(keyStore); params.setRevocationEnabled(true); CertPath certPath CertificateFactory.getInstance(X.509) .generateCertPath(Arrays.asList(chain)); validator.validate(certPath, params);7. 最佳实践总结经过多个项目的实践验证我总结出以下经验开发阶段可以使用快速方案绕过验证但发布前必须移除生产环境推荐使用CertificatePinner进行证书锁定对于自签名证书应将CA证书打包到应用中定期检查服务器证书有效期建议设置自动提醒考虑使用证书透明度Certificate Transparency监控在Glide配置中统一设置超时时间建议连接10s读写20s一个完整的生产级配置示例GlideModule public class ProductionGlideModule extends AppGlideModule { Override public void registerComponents(Context context, Glide glide, Registry registry) { OkHttpClient client new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) .certificatePinner(getCertificatePinner()) .addInterceptor(new UserAgentInterceptor()) .build(); registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client)); } private CertificatePinner getCertificatePinner() { return new CertificatePinner.Builder() .add(*.yourdomain.com, sha256/YourPrimaryKey) .add(*.yourdomain.com, sha256/YourBackupKey) .build(); } }