• 检测SSL,从http切换到https的相关代码,基于Retrofit,kotlin


    原来项目用的http接口,因为已经有人在使用了,所以要升级时,需要在同一个时间点切换

    一个检查http接口是否通畅的类,基于rxjava

    import com.google.gson.Gson
    import io.reactivex.Observable
    import io.reactivex.android.schedulers.AndroidSchedulers
    import io.reactivex.functions.BiFunction
    import io.reactivex.functions.Function
    import io.reactivex.schedulers.Schedulers
    import me.goldze.mvvmhabit.utils.KLog
    import me.goldze.mvvmhabit.utils.SPUtils
    import net.topstarts.base.base.TSBaseResponse
    import net.topstarts.base.config.TSConfigSetting
    import net.topstarts.base.okhttp.LogInterceptor
    import okhttp3.OkHttpClient
    import retrofit2.Retrofit
    import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
    import retrofit2.converter.gson.GsonConverterFactory
    import retrofit2.http.GET
    import retrofit2.http.Url
    import java.util.HashMap
    import java.util.concurrent.TimeUnit
    
    /**
     *          检查SSL是否可用
     *  1.先检查  http 可用 则是http
     *  2. 如果http不可用, 再检查 https 可用才是 https
     *  3.如果 2 不可用,则重复 1 , 2 步骤
     *
     */
    class CheckSSLStateWorker() {
    
        private var retrofit: Retrofit? = null
        private val headers by lazy {
            HashMap<String, String>()
        }
    
        private val checkUrlBySSL by lazy {
            "https://xxxxxx"
        }
    
        private val checkUrlByHttp by lazy {
            "http://xxxx"
        }
    
        // 总共的最大测试次数
        private val maxTestSSLNum = 3
    
        // 现在测试的是第几次
        private var testSSLNum = 0
    
        init {
            buildNetWork()
        }
    
        val gson = Gson()
    
        /**
         *  这里是并发访问
         */
        /*override fun onHandleIntent(intent: Intent?) {
            val httpTestObservable =
                retrofit?.create(ApiService::class.java)?.checkIsSSL(checkUrlByHttp)?.subscribeOn(Schedulers.io())
                    ?.doOnNext {
                        if (it.isOk) {
                            it.msg = "http"
                        }
                    }?.retryWhen {
                        it.zipWith(Observable.range(1, maxTestSSLNum), BiFunction<Throwable, Int, Int> { t1, t2 ->
                            t2
                        }).flatMap(object : Function<Int, Observable<Long>> {
                            override fun apply(retryCount: Int): Observable<Long> {
                                KLog.i("------------- retryCount =$retryCount " + " thread ${Thread.currentThread().name}")
                                return Observable.timer(5, TimeUnit.SECONDS)
                            }
                        })
                    }?.observeOn(
                        AndroidSchedulers.mainThread()
                    )
            val sslTestObservable =
                retrofit?.create(ApiService::class.java)?.checkIsSSL(checkUrlBySSL)?.subscribeOn(Schedulers.io())
                    ?.doOnNext {
                        if (it.isOk) {
                            it.msg = "https"
                        }
                    }?.retryWhen {
                        it.zipWith(Observable.range(1, maxTestSSLNum), BiFunction<Throwable, Int, Int> { t1, t2 ->
                            t2
                        }).flatMap(object : Function<Int, Observable<Long>> {
                            override fun apply(retryCount: Int): Observable<Long> {
                                KLog.i("------------- retr0yCount =$retryCount " + " thread ${Thread.currentThread().name}")
                                return Observable.timer(5, TimeUnit.SECONDS)
                            }
                        })
                    }?.observeOn(
                        AndroidSchedulers.mainThread()
                    )
            var isNormal = false
            Observable.mergeDelayError(httpTestObservable, sslTestObservable).firstElement()
                .subscribe({
               isNormal = true
                KLog.i("--------------> ${gson.toJson(it)}")
                showToastMsg(gson.toJson(it))
                if (it.msg.equals("https")) {
                    EventBus.getDefault().post("SSL://true")
                } else {
                    EventBus.getDefault().post("SSL://false")
                }
            }, {
                it.printStackTrace()
                KLog.i("--------------> 出错")
                if (!isNormal) {
                    EventBus.getDefault().post("SSL://error")
                }
            }, {
                if (!isNormal) {
                    EventBus.getDefault().post("SSL://error")
                }
                KLog.i("--------------> 完成")
            })
        }*/
    
        //  默认 http 协议
        val isHttpsByUser by lazy {
            SPUtils.getInstance().getBoolean(
                TSConfigSetting.APP_SETTING_CHANGE_HTTPS, false
            )
            //        true
        }
    
        /**
         *  这里是串行访问
         *     // 请求码
         *  @param resultCall    1 https 2 http  3 error
         *  @param requestStr    请求码字符串
         */
        fun onHandleWork(resultCall: (Int, String) -> Unit, requestStr: String) {
            val httpTestObservable = retrofit?.create(ApiService::class.java)?.checkIsSSL(checkUrlByHttp)?.doOnNext {
                if (it.isOk) {
                    KLog.i("-------------http doOnNext =$it ")
                    it.msg = "http"
                }
            }
    
            val sslTestObservable = retrofit?.create(ApiService::class.java)?.checkIsSSL(checkUrlBySSL)?.doOnNext {
                if (it.isOk) {
                    KLog.i("-------------https doOnNext =$it ")
                    it.msg = "https"
                }
            }
            var isNormal = false
            // 依据默认的,确定先检查哪一个,以加快检查速度
            val observableStart = if (isHttpsByUser) {
                Observable.concatArrayDelayError(sslTestObservable, httpTestObservable)
            } else {
                Observable.concatArrayDelayError(httpTestObservable, sslTestObservable)
            }
    
            observableStart.subscribeOn(Schedulers.io()).retryWhen {
                KLog.i("-------------retryWhen =${it} ")
                it.zipWith(Observable.range(1, maxTestSSLNum), BiFunction<Throwable, Int, Int> { t1, t2 ->
                    t2
                }).flatMap(object : Function<Int, Observable<Long>> {
                    override fun apply(retryCount: Int): Observable<Long> {
                        KLog.i("------------- retr0yCount =$retryCount " + " thread ${Thread.currentThread().name}")
                        return Observable.timer(5, TimeUnit.SECONDS)
                    }
                })
            }.firstElement().observeOn(
                AndroidSchedulers.mainThread()
            ).subscribe({
                isNormal = true
                KLog.i("--------------> ${gson.toJson(it)}")
    
                if (it.msg.equals("https")) {
                    SPUtils.getInstance().put(TSConfigSetting.APP_SETTING_CHANGE_HTTPS, true)
                    //                eventBus.post("SSL://true::")
                    resultCall.invoke(1, requestStr)
                } else {
                    SPUtils.getInstance().put(TSConfigSetting.APP_SETTING_CHANGE_HTTPS, false)
                    //                eventBus.post("SSL://false::")
                    resultCall.invoke(2, requestStr)
                }
            }, { // 默认 HTTP
                it.printStackTrace()
                KLog.i("--------------> 出错")
                if (!isNormal) {
                    SPUtils.getInstance().put(TSConfigSetting.APP_SETTING_CHANGE_HTTPS, false)
                    //                eventBus.post("SSL://error::")
                    resultCall.invoke(3, requestStr)
                }
            }, {
                if (!isNormal) {
                    SPUtils.getInstance().put(TSConfigSetting.APP_SETTING_CHANGE_HTTPS, false)
                    //                eventBus.post("SSL://error::")
                    resultCall.invoke(3, requestStr)
                }
                KLog.i("--------------> 完成")
            })
        }
    
        private fun buildNetWork() {
            //        headers.put("X-Tops-Src", "android")
            //            headers.put("version", getVersionName(mContext));
            //        headers.put("Content-Type", "application/json;charset=UTF-8")
    
            val okHttpClient = OkHttpClient.Builder().addInterceptor(LogInterceptor()).connectTimeout(
                20, TimeUnit.SECONDS
            )
                //.readTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
                //                            .callTimeout(60, TimeUnit.SECONDS)
                .build()
            retrofit = Retrofit.Builder().client(okHttpClient).addConverterFactory(GsonConverterFactory.create()) // 加上这句话
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).baseUrl("").build()
        }
    
        private interface ApiService {
            @GET
            abstract fun checkIsSSL(
                @Url url: String
            ): Observable<TSBaseResponse<Any>>
        }
    }

    上面引用的 LogInterceptor   注意点:::后台可以把HTTP接口请求,从https返回

    import android.text.TextUtils
    import com.google.gson.GsonBuilder
    import me.goldze.mvvmhabit.utils.KLog
    import me.goldze.mvvmhabit.utils.SingletionHolder
    import net.topstarts.module_base.BuildConfig
    import okhttp3.Interceptor
    import okhttp3.Request
    import okhttp3.Response
    import okio.Buffer
    import java.net.URLDecoder
    import java.nio.charset.Charset
    import java.util.concurrent.ConcurrentHashMap
    import java.util.concurrent.TimeUnit
    
    
    /**
     * Description: <r>
     * Author:Caosq<r>
     * Date:2019/10/29 15:07
     */
    class LogInterceptor : Interceptor {
        // 不对 HTML $ = 进行转义
        val gson = GsonBuilder().disableHtmlEscaping().create()
        private val UTF8 = Charset.forName("UTF-8")
    
        // 为了打印JSON
        val headerMap = ConcurrentHashMap<String, String>()
        val bodyMap = ConcurrentHashMap<String, String>()
    
        override fun intercept(chain: Interceptor.Chain): Response {
            // 切换 https 或者 http 协议
            val request = chain.request()
    
            val isNeddPrint = needPrint(request.url().toString())
            if (!isNeddPrint) {
                KLog.i(
                    "发送请求: method:${request.method()}
    url:${request.url()}
    请求头:${gson.toJson(
                        headerMap
                    )}"
                )
                val response = chain.proceed(request)
                //request= ${gson.toJson(request)}response= ${gson.toJson(response)}
                return response
            }
            val requestBody = request.body()
            var body: String? = null
            requestBody?.let {
                val buffer = Buffer()
                requestBody.writeTo(buffer)
                var charset: Charset? = UTF8
                val contentType = requestBody.contentType()
                contentType?.let {
                    charset = contentType.charset(UTF8)
                }
                body = buffer.readString(charset!!)
            }
            headerMap.clear()
            bodyMap.clear()
            //---------------
            var bodyJson: String = ""
            if (!TextUtils.isEmpty(body)) {
                //            val arrayStr = URLDecoder.decode(body, "UTF-8")?.split("&")
                val arrayStr = body?.split("&")
                //            val arrayStr = body?.split("&")
                arrayStr?.forEach {
                    val nameValue = it.split("=")
                    try {
                        //如果是文件流 这里会抛异常
                        bodyMap.put(nameValue[0], URLDecoder.decode(nameValue[1], "UTF-8"))
                    } catch (e: Exception) {
                        // 如果是文件流 可能没有KEY
                        if (nameValue.size > 1) {
                            bodyMap.put(nameValue[0], nameValue[1])
                            bodyJson = gson.toJson(bodyMap)
                        } else {
                            bodyJson = nameValue[0]
                            //                        val type= object : TypeToken<Map<String?, String>>() {}.getType()
                            //                        val map = gson.fromJson<Map<String, String>>(nameValue[0],type)
                            //                        bodyMap.putAll(map)
                        }
                    }
                }
            }
            //------------------
            request.headers().let {
                val names = it.names()
                for (name in names) {
                    headerMap.put(name, it.get(name) ?: "")
                }
            }
    
            if (isNeddPrint) {
                val json = gson.toJson(bodyMap)
                KLog.i(
                    "发送请求: method:${request.method()}
    url:${request.url()}
    请求头:${gson.toJson(
                        headerMap
                    )}
    请求参数: $bodyJson"
                )
            }
    
            val startNs = System.nanoTime()
            val response = chain.proceed(request)
            val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
    
            val responseBody = response.body()
            val rBody: String
    
            val source = responseBody!!.source()
            source.request(java.lang.Long.MAX_VALUE)
            val buffer = source.buffer()
    
            var charset: Charset? = UTF8
            val contentType = responseBody.contentType()
            contentType?.let {
                try {
                    charset = contentType.charset(UTF8)
                } catch (e: Exception) {
                    KLog.e(e.message)
                }
            }
            rBody = buffer.clone().readString(charset!!)
            if (isNeddPrint) {
                //防止 body 为 null 如果砬到后台有将请求转发后,再返回时,会导致 response.request().url()与 开始的 request.url()不一样,但response.request().toString() /tag 里有相应的记录
                if (request.url().equals(response.request().url())) {
                    KLog.i(
                        "收到响应: code: ${response.code()}
    请求url:${response.request()
                            .url()}
    Response: ${rBody}  花费时间: $tookMs 毫秒"
                    )
                } else {
                    KLog.i(
                        "收到响应: code: ${response.code()}
    请求url:${response.request().url()}
    ${response.request()
                            .toString()}
    Response: ${rBody}  花费时间: $tookMs 毫秒"
                    )
                }
            }
            return response
        }
    
    
        fun needPrint(currentUrl: String): Boolean {
            var needPrint = true
            noPrintUrl.forEach {
                if (currentUrl.contains(it)) {
                    needPrint = false
                    return needPrint
                }
            }
            return needPrint
        }
    
        //    val noPrintUrl = arrayListOf<String>("platform/m/v1/banner", "platform/m/v1/permission","platform/m/v1/user/list", "oss/upload")
        // 文件上传 流下载时不能加打印会导致   System.err: java.net.ProtocolException: unexpected end of stream 字节流结束前后不一致
        val noPrintUrl = arrayListOf<String>("platform/m/v1/banner", "oss/upload", "home/uaa/captcha/get")
        //    val noPrintUrl = arrayListOf<String>("platform/m/v1/banner")
    
        //    companion object : SingletionHolder<LogInterceptor, Boolean>(::LogInterceptor)
    }

    检查SSL 是http 或者 https后,使用Retrofit拦截器 切换所有的APP接口,(这里是单例,还可以由用户手动切换)

    import me.goldze.mvvmhabit.utils.KLog
    import me.goldze.mvvmhabit.utils.SPUtils
    import me.goldze.mvvmhabit.utils.SingletionHolder
    import net.topstarts.base.config.TSConfigSetting
    import net.topstarts.module_base.BuildConfig
    import okhttp3.Interceptor
    import okhttp3.Request
    import okhttp3.Response
    
    /**
     * 切换 ssl的 拦截
     */
    class ChangeSSLInterceptor private constructor(private val isHttps: Boolean) : Interceptor {
    
        var isHttpsByUser: Boolean = isHttps
    
        //
        var isSelByUser: Boolean = SPUtils.getInstance().getBoolean(TSConfigSetting.APP_SETTING_CHANGE_HTTPS_By_USER, false)
    
        override fun intercept(chain: Interceptor.Chain): Response {
            // 切换 https 或者 http 协议
            val request = changeHttpsOrHttpAgreementt(chain.request())
    
            return chain.proceed(request)
        }
    
        /**
         *   切换 https 或者 http 协议
         */
        private fun changeHttpsOrHttpAgreementt(request: Request): Request {
            var url = request.url().toString()
            if (BuildConfig.FLAVOR.equals("dev_rep")) {//内网还是 http
                val newBuilder = request.newBuilder()
                return newBuilder.url(url).build()
            }
            if (url.contains("oss/upload")) { // 上传图片地址 固定为 https 下载图片,固定为 http
                url = "${BuildConfig.fileDomain}oss/upload"
                val newBuilder = request.newBuilder()
                return newBuilder.url(url).build()
            }
            //        KLog.i("-------------------->>isHttpsByUser= $isHttpsByUser $url")
            if (isHttpsByUser) { // 是 HTTPS
                if (!url.contains("https://")) {
                    url = url.replace("http://", "https://")
                } else {
                    return request
                }
            } else { //是http
                if (url.contains("https://")) {
                    url = url.replace("https://", "http://")
                } else {
                    return request
                }
            }
            val newBuilder = request.newBuilder()
            return newBuilder.url(url).build()
        }
    
        /**
         * @param isUserSel true是用户手动选择,一旦用户手动选择了,就不再使用SSL检查的了
         */
        fun changeHttpsByUser(isHttps: Boolean, isUserSel: Boolean = false) {
            if (isSelByUser || isUserSel) {
                if (!isUserSel) {
                    return
                }
                isHttpsByUser = isHttps
                if (!isSelByUser) {
                    isSelByUser = true
                }
            } else {
                isHttpsByUser = isHttps
                if (isUserSel) {
                    isSelByUser = true
                }
            }
        }
    
        /**
         * 重新设成代码控投制
         */
        fun resetSelByUserToFalse() {
            isSelByUser = false
        }
    
        companion object : SingletionHolder<ChangeSSLInterceptor, Boolean>(::ChangeSSLInterceptor)
    }
  • 相关阅读:
    令人惊艳的算法——蒙特卡洛采样法
    肺炎确诊人数增长趋势拟合和预测(截止1月28日)
    「07」回归的诱惑:深入浅出逻辑回归
    实现一个简易版Webpack
    nrm——快速切换npm源
    7个有用的JavaScript技巧
    JSON Web Token 是什么?
    【译】强化表单的9个Vue输入库
    【译】如何使用Vue过渡效果来提升用户体验
    【译】JavaScript Source Maps浅析
  • 原文地址:https://www.cnblogs.com/caosq/p/13273770.html
Copyright © 2020-2023  润新知