首页 > Web开发 > 详细

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

时间:2020-07-09 15:17:12      阅读:61      评论:0      收藏:0      [点我收藏+]

原来项目用的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: <\br>
 * Author:Caosq<\br>
 * 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()}\nurl:${request.url()}\n请求头:${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()}\nurl:${request.url()}\n请求头:${gson.toJson(
                    headerMap
                )}\n请求参数: $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()}\n请求url:${response.request()
                        .url()}\nResponse: ${rBody}  花费时间: $tookMs 毫秒"
                )
            } else {
                KLog.i(
                    "收到响应: code: ${response.code()}\n请求url:${response.request().url()}\n${response.request()
                        .toString()}\nResponse: ${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)
}

 

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

原文:https://www.cnblogs.com/caosq/p/13273770.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!