目录

  • 前言
  • 启用https后白屏(证书错误)
  • 修改处理
  • WebView中Http和Https混合问题
    • 处理办法
    • Webview的几种内容加载模式
  • 证书配置或处理
  • https请求的证书
    • okhttp进行请求:
    • HttpsURLConnection
    • 忽略证书

前言

原有项目中有部分界面是用webview展现的h5页面,一直以来都使用的http地址,但有些情况下,用户dns被劫持,页面上出现了一些广告的内容,或者页面就是白屏,总结起来还是因为使用http,页面内容被劫持修改,修改后的内容要么多出广告,要么被修改得加载不出来,因此项目中自然就需要加载https的地址。同时修改为https之后,出现问题的那些用户也能正常的显示出h5页面内容。

启用https后白屏(证书错误)

然而,在新版本上线后,有客户反馈oppo(8.1)、vivo等用户反馈app中页面白屏,单位的oppo和vivo验证都没有问题。最后小伙伴用模拟器复现了,同时也看到错误日志:

此处的日志是在WebChromeClient的重实现打出来的(代码已经修改过):

  @Overridepublic void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {GenseeLog.e(TAG,"onReceivedSslError sslErrorHandler = [" + sslErrorHandler + "], sslError = [" + sslError + "]");_onReceivedError();super.onReceivedSslError(webView, sslErrorHandler, sslError);}

修改处理

得知是在WebChromeClient的onReceivedSslError重实现中响应了相关的错误,在onReceivedSslError中调用sslErrorHandler.process(); 就可以了:忽略证书问题。

     public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {GenseeLog.e(TAG,"onReceivedSslError sslErrorHandler = [" + sslErrorHandler + "], sslError = [" + sslError.getPrimaryError() + "]");_onReceivedError();sslErrorHandler.proceed();
//                super.onReceivedSslError(webView, sslErrorHandler, sslError);}

此处注意:不要调用super.onReceivedSslError(webView, sslErrorHandler, sslError),super的实现是sslErrorHandler.cancel();,终止访问。

   public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {handler.cancel();}

注意:文本是通过忽略证书的问题处理的,但从实质性的安全来说并没起到作用。确认我们的证书是无误的。唯独几个oppo(系统8.1)和vivo用户有问题,目前没有得到真机的验证,推测还是系统证书下载问题。有知道的老铁请留言,感谢之。
正确建议做法是做证书校验:

webView.setWebViewClient(new  WebViewClient() {overridepublic void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {String msg;switch(error.getPrimaryError()) {case: SslError.SSL_DATE_INVALIDmsg = "证书日期无效"break;case: SslError.SSL_EXPIREDmsg =  "证书已过期。"break;case: SslError.SSL_IDMISMATCH msg = "主机名不匹配。"break;case: SslError.SSL_INVALID msg = "发生一般错误"break;case: SslError.SSL_MAX_ERRORmsg = "不同SSL错误的数量。"break;case: SslError.SSL_NOTYETVALIDmsg =  "证书尚未生效。"break;case: SslError.SSL_UNTRUSTEDmsg =  "证书颁发机构不受信任。" // 自定义证书会执行到这个分支来break;default:msg ="SSL证书错误,错误码:"+ error.getPrimaryError();}Log.i("SSL错误:" + msg)if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) {// 证书颁发机构不受信任,则我们需要判断一下是否是我们自己的自定义证书,是的话就忽略这个错误CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");X509Certificate certificate = certificateFactory.generateCertificate(resources.openRawResource(R.raw.xxx)) ;Field mX509CertificateFiled = SslCertificate.getClass().getDeclaredField("mX509Certificate");mX509CertificateFiled .setAccessible( true);X509Certificate mX509Certificate = mX509CertificateFiled.get(error.certificate());val certificates = HandshakeCertificates.Builder().addTrustedCertificate(certificate) // 信任指定的自定义证书.addPlatformTrustedCertificates()  // 信任系统的预装证书,如果不信任系统证书的话,比如在访问https://m.baidu.com时将会出错.build()try {certificates.trustManager.checkServerTrusted(new X509Certificate []{mX509Certificate}), "RSA")Log.i("是我们的自定义证书")handler.proceed()} catch (e: java.lang.Exception) {Log.e(e, "非法证书")handle.cancel()}}} else {super.onReceivedSslError(view, handler, error)}}
});

WebView中Http和Https混合问题

在一个页面通过http请求的,但页面内有https的资源,后者页面是通过https访问的,但页面内有http的资源,这样的混合,一般后者都存在问题,当前前者也可能存在问题。

处理办法

在webview加载页面之前,设置加载模式为MIXED_CONTENT_ALWAYS_ALLOW

       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);}

在Android5.0之前,系统默认是采用的MIXED_CONTENT_ALWAYS_ALLOW模式,即总是允许WebView同时加载Https和Http;而从Android5.0开始,默认用MIXED_CONTENT_NEVER_ALLOW模式,即总是不允许WebView同时加载Https和Http。

如果都是自家的网页,那这个问题还是从源头上进行处理,页面不要使用混合的方式。

官网给出的建议是,为了安全考虑,使用 MIXED_CONTENT_NEVER_ALLOW模式,但是在实际引用中,当我们的服务器已经升级到Https,但是一些页面的资源是第三方的,我们不一定能要求第三方也都升级到Https,所以我们只能根据系统版本,用代码去设置加载模式为MIXED_CONTENT_ALWAYS_ALLOW。

Webview的几种内容加载模式

从Android5.0开始,当一个安全的站点(https)去加载一个非安全的站点(http)时,需要配置Webview加载内容的混合模式,一共有如下三种模式:

  • MIXED_CONTENT_NEVER_ALLOW:Webview不允许一个安全的站点(https)去加载非安全的站点内容(http),比如,https网页内容的图片是http链接。强烈建议App使用这种模式,因为这样更安全。
  • MIXED_CONTENT_ALWAYS_ALLOW:在这种模式下,WebView是可以在一个安全的站点(Https)里加载非安全的站点内容(Http),这是WebView最不安全的操作模式,尽可能地不要使用这种模式。
  • MIXED_CONTENT_COMPATIBILITY_MODE:在这种模式下,当涉及到混合式内容时,WebView会尝试去兼容最新Web浏览器的风格。一些不安全的内容(Http)能被加载到一个安全的站点上(Https),而其他类型的内容将会被阻塞。这些内容的类型是被允许加载还是被阻塞可能会随着版本的不同而改变,并没有明确的定义。这种模式主要用于在App里面不能控制内容的渲染,但是又希望在一个安全的环境下运行。

证书配置或处理

如果需要配置证书的情况下,
在res目录下创建一个xml文件目录,再创建一个network_security_config.xml(名字可随意),然后就是在这个文件中写入一些关于https的配置,接着把network_security_config配置到Anroidmanifest的Application节点属性中,如下:

<applicationandroid:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:networkSecurityConfig="@xml/network_security_config"></application>

我们的xxx.crt的证书文件按照规范,需要放到res/raw/目录下面。接着配置到network_security_config文件中。配置证书,有两种方式,如下:

  1. base-config
    表示应用的访问的所有域名的资源都信任我们指定的xxx证书
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config><trust-anchors><certificates src="@raw/xxx" /></trust-anchors></base-config>
</network-security-config>
  1. domain-config
    使用domain-config限制了gensee.com或它的子域名都信任我们指定的xxx证书
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><domain-config><domain includeSubdomains="true">gensee.com</domain><trust-anchors><certificates src="@raw/xxx"/></trust-anchors></domain-config>
</network-security-config>
  1. 指定信任预安装的证书
    配置我们要信任什么证书,直接使用@raw/xxx为配置信任自定义的证书,如果要指定信任预安装的证书,需要另外指定,预安装的证书有系统和用户两种类型,如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config><trust-anchors><certificates src="@raw/custom_ca" /> <!--信任自定义的CA证书--><certificates src="system" /> <!--信任系统预装的CA证书--><certificates src="user" />   <!--信任用户安装的CA证书--></trust-anchors></base-config>
</network-security-config>

https请求的证书

okhttp进行请求:

val builder = OkHttpClient.Builder()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {val certificateFactory: CertificateFactory = CertificateFactory.getInstance("X.509")val certificate = certificateFactory.generateCertificate(resources.openRawResource(R.raw.xxx)) as X509Certificateval certificates = HandshakeCertificates.Builder().addTrustedCertificate(certificate) // 信任指定的自定义证书.addPlatformTrustedCertificates()  // 信任系统的预装证书,如果不信任系统证书的话,比如在访问https://m.baidu.com时将会出错.build()builder.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager)
}
val okHttpClient = builder.build()

HttpsURLConnection

    // Load CAs from an InputStream// (could be from a resource or ByteArrayInputStream or ...)val cf: CertificateFactory = CertificateFactory.getInstance("X.509")// From https://www.washington.edu/itconnect/security/ca/load-der.crtval caInput: InputStream = BufferedInputStream(FileInputStream("load-der.crt"))val ca: X509Certificate = caInput.use {cf.generateCertificate(it) as X509Certificate}System.out.println("ca=" + ca.subjectDN)// Create a KeyStore containing our trusted CAsval keyStoreType = KeyStore.getDefaultType()val keyStore = KeyStore.getInstance(keyStoreType).apply {load(null, null)setCertificateEntry("ca", ca)}// Create a TrustManager that trusts the CAs inputStream our KeyStoreval tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {init(keyStore)}// Create an SSLContext that uses our TrustManagerval context: SSLContext = SSLContext.getInstance("TLS").apply {init(null, tmf.trustManagers, null)}// Tell the URLConnection to use a SocketFactory from our SSLContextval url = URL("https://certs.cac.washington.edu/CAtest/")val urlConnection = url.openConnection() as HttpsURLConnectionurlConnection.sslSocketFactory = context.socketFactoryval inputStream: InputStream = urlConnection.inputStreamcopyInputStreamToOutputStream(inputStream, System.out)

忽略证书

/** 获取一个SSLSocketFactory */
val sSLSocketFactory: SSLSocketFactoryget() = try {val sslContext = SSLContext.getInstance("SSL")sslContext.init(null, arrayOf(trustManager), SecureRandom())sslContext.socketFactory} catch (e: Exception) {throw RuntimeException(e)}/** 获取一个忽略证书的X509TrustManager */
val trustManager: X509TrustManagerget() = object : X509TrustManager {override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) { }    override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) { }  override fun getAcceptedIssuers(): Array<X509Certificate> { return arrayOf() }}

将这两个对象设置给okhttp或HttpsURLConnection即可完成证书忽略。

https://blog.csdn.net/android_cai_niao/article/details/108065766
https://blog.csdn.net/luofen521/article/details/51783914

Android WebView https白屏、Http和Https混合问题、证书配置和使用相关推荐

  1. WebView的白屏检测与处理

    /   今日科技快讯   / 近日,中国空间站阶段飞行任务总指挥部在酒泉卫星发射中心召开神舟十二号载人飞行任务新闻发布会,通报此次任务有关情况.中国载人航天工程办公室主任助理季启明在会上介绍,中国将于 ...

  2. Android启动页白屏背景图拉伸的解决方案

    Android启动页白屏&背景图拉伸的解决方案 1.前言 2.问题点 3.代码及说明 4.注意点 1.前言 新项目的闪屏页面用到一张大图,UI那边要求那张图片填充全屏,而且不能压缩或拉伸. 然 ...

  3. android广告页白屏_Android 启动页白屏解决方案

    当我们打开app的时候是不是会有一瞬间的白屏然后再进入主活动,虽然这并不会造成什么不好的后果,但是感觉用户体验就不是很好.像网易云音乐等等,打开一瞬间就显示了他们的loge,无缝衔接,没有白屏,怎么 ...

  4. Android应用启动白屏处理

    信大家一定遇到过某些App在手机桌面打开时会出现短暂或者几秒钟的白屏情况吧,没错那是应用程序启动后系统默认的背景色,此时应用的第一个Activity还未被加载出来,所以不管怎么设置第一个Activit ...

  5. android 加载图片 白屏,android启动页白屏和图片自适应问题

    android启动页白屏和图片自适应问题,自适应官方给的说法也只是做多套图去适配不同的分辨率,本文通过几个设置几行代码即可解决启动页短暂白屏和自适应问题. 网上的说法是通过启动页代码裁切图片或者在Im ...

  6. android广告页白屏_年度整理!2056页《大厂安卓岗面试真题解析合集》火爆全网...

    前言 2020年还有最后一个月就结束了,时间一眨眼就过去了.今年面试有没有被面试官虐呢,明年跳槽想跳去哪个大厂呢,这是个问题.说实话,今年我面试也被虐了,为了明年能找到一份心怡的工作,特地的从朋友那里 ...

  7. webView显示白屏的问题

    webView加载酷狗音乐或者带视频的一些较大网页时显示白屏的问题修复记录: String mp4Path = "http://yoo.qq.com/m/video.htmlid=w0031 ...

  8. android app启动白屏优化

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/wuliang756071448/article/details/74067058 可以看到启动的时候 ...

  9. android广告页白屏_android 白屏

    从首篇介绍我为什么写文章到现在已经过去好久.......(懒),但从此刻开始,不出意外情况每周给大家至少写一篇文章,就按平时app开发流程以及本人遇到的问题,从app欢迎页面开始吧! 一:为什么会有欢 ...

最新文章

  1. 360金融首席科学家张家兴:只靠AI Lab做不好AI中台 | 独家专访
  2. 20181120-1 每周例行报告
  3. 获取web.py上面的示例code
  4. 操作系统复习笔记(四)
  5. 江西计算机一级考试教程,江西省2019年下半年计算机一级考试复习教程:计算机基础及MS Office应用上机指导...
  6. 【转】CentOS 6.6 升级GCC G++ (当前最新版本为v6.1.0) (完整)
  7. 递归调用方法时栈内存是如何变化的?(使用内存图演示递归调用过程)
  8. 《你不常用的c#之五》:Thread与ThreadPool的内存之战
  9. git 常用命令(含删除文件)
  10. 程序清单4.1_talkback.c程序_《C Primer Plus》P60
  11. 蓝色起源起诉NASA,不服其将月球着陆器合同授予SpaceX
  12. 建立基于虚拟用户的vsftpd
  13. 【原创】EXCEL公式经验(1)---如何拆解/拆分字符串为多列和多行,但是注意不要超过EXCEL字符串最长长度限制!
  14. 高考满分作文《细雨湿衣看不见,闲花落地听无声》 之 金庸新版
  15. SQL SERVER RBAC权限管理表
  16. frp服务端(frps) 安装及使用
  17. DELL灵越笔记本插上耳机后,只有外放耳机无声音的解决方法。
  18. 情商决定了工作方面的成就
  19. Oracle数据库(索引、视图、伪列与伪表)
  20. Windows11/Win10+VS2019下载编译WebRTC源码

热门文章

  1. ruby推送示例_Ruby直到示例循环
  2. Java SecurityManager getThreadGroup()方法与示例
  3. 清除缓存 c语言_如何用C语言设置,清除和切换单个位?
  4. SpringBoot 优雅的参数效验!
  5. 实战:分布式锁详解与代码
  6. Redis从入门到精通|干货篇
  7. QT5更改应用程序图标
  8. Python手写神经网络实现3层感知机
  9. 在pandas中遍历DataFrame行
  10. nifi 实现数据库到数据库之间数据同步