注入Koin的OkHttpClient丢失授权标头

时间:2020-10-30 22:58:50

标签: android kotlin authorization okhttp koin

我正在将DI添加到现有项目中,在过程中我遇到了标题Authorization从请求中消失的问题。 Retrofit / OkHttp没有任何异常或日志。我的依赖项是:

implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.okhttp:okhttp:2.7.5'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'org.koin:koin-android:2.1.3'

我使用provideClient创建http客户端:

class OkHttpProvider private constructor() {

    companion object {

        fun provideClient(credentials: UsernamePasswordCredentials? = null, context: Context): OkHttpClient {
            val client = OkHttpClient.Builder()
            // logs
            if (BuildConfig.DEBUG) {
                client.addInterceptor(
                        HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
                )
            }

            if (credentials != null) {
                val creds = Credentials.basic(credentials.userName, credentials.password)
                val headerInterceptor = Interceptor { chain ->
                    var request = chain.request()
                    val headers = request
                            .headers()
                            .newBuilder()
                            .add("Authorization", creds)
                            .build()
                    request = request.newBuilder().headers(headers).build()
                    chain.proceed(request)
                }
                //client.addInterceptor(AccessTokenInterceptor(credentials))
                client.addInterceptor(headerInterceptor)
            }

            client
                    .callTimeout(60L, TimeUnit.SECONDS)
                    .connectTimeout(10L, TimeUnit.SECONDS)
                    .readTimeout(60L, TimeUnit.SECONDS)
                    .writeTimeout(60L, TimeUnit.SECONDS)
                    .sslSocketFactory(getSslContext().socketFactory).hostnameVerifier { _, _ -> true }

            client.addInterceptor(ChuckInterceptor(context))

            return client.build()
        }

        private fun getSslContext(): SSLContext {
            ...implementation...
        }
    }
}

我的http客户端和翻新模块如下:

object HttpClientModule {
    val module = module {
        single(named(COMMON)) {
            OkHttpProvider.provideClient(
                    get<SharedPreferenceManager>().getUserCredentials(),
                    androidContext()
            )
        }
        ...other versions...
    }

    const val COMMON = "common"
}

object ApiModule {
    val module = module {
        single {
            RetrofitFactory.getServiceInstance(
                    ApiService::class.java,
                    get<SharedPreferenceManager>().getString(LocalDataSource.BUILD_OPTION_API, ""),
                    get(named(HttpClientModule.COMMON))
            )
        }
        ...other apis...
    }
}

object RetrofitFactory {
    const val GEO_URL = "http://maps.googleapis.com/maps/api/"

    fun <T> getServiceInstance(
            clazz: Class<T>,
            url: String = GEO_URL,
            client: OkHttpClient
    ): T = getRetrofitInstance(url, client).create(clazz)

    private fun getRetrofitInstance(
            url: String,
            client: OkHttpClient
    ) = Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .build()
}

应用程序开始与“管理员”用户一起使用,并且当用户开始使用电话和短信登录时,并且在共享首选项中保存了一些凭据,并且当用户从短信及其新用户输入代码时,请求使用“管理员”授权标头发送凭据保存在共享首选项中。该应用发送两个请求后,其中未显示授权标头。我在查克(Chuck)看到了它,甚至用查尔斯(Charles)再次检查了它。

为解决此问题,我尝试了几种解决方案。首先,我将http客户端的注入从single更改为factory,但这没有用。其次,我在问题上进行了搜索,但没有提及这种现象。第三,我根据this的文章写了AccessTokenInterceptor,并且还用日志覆盖了所有内容。我注意到拦截器在正常情况下可以正常工作,但是当缺少Authorization标头时,不会调用方法intercept。这可能是默认headerInterceptor也不起作用的原因。第四,我升级了Retrofit和OkHttp版本,这也没有帮助。

我注意到了有关该错误的有趣事情:如果我在Retrofit丢失了Authorization标头后重新启动应用程序,则该应用程序可以正常运行,并且使用正确的令牌正确记录了测试用户。在不重新启动应用程序的情况下进行任何尝试重新登录都会失败。也许有人遇到类似的问题,或者知道这里发生了什么,欢迎任何想法。

1 个答案:

答案 0 :(得分:0)

我终于找到了解决这个问题的方法。问题是用户凭据在创建时仅传递给provideClient一次。此时,用户以admin身份登录,并且标准用户凭据为空,因此ApiService的http客户端被创建为没有Authorization标头。

为解决此问题,我更改了AccessTokenInterceptor表单articleHttpClientType是一个枚举,用于选择需要使用的凭据):

class AccessTokenInterceptor(
        private val sharedPreferenceManager: SharedPreferenceManager,
        private val clientType: OkHttpProvider.HttpClientType
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val credentials = getUserCredentials(clientType)
        if (credentials != null) {
            val accessToken = Credentials.basic(credentials.userName, credentials.password)
            val request = newRequestWithAccessToken(chain.request(), accessToken)

            return chain.proceed(request)
        } else {
            return chain.proceed(chain.request())
        }
    }

    private fun getUserCredentials(clientType: OkHttpProvider.HttpClientType): UsernamePasswordCredentials? {
        return when (clientType) {
            OkHttpProvider.HttpClientType.COMMON -> sharedPreferenceManager.getUserCredentials()
            OkHttpProvider.HttpClientType.ADMIN -> ServiceCredentialsUtils.getCredentials(sharedPreferenceManager)
        }
    }

    private fun newRequestWithAccessToken(@NonNull request: Request, @NonNull accessToken: String): Request {
        return if (request.header("Authorization") == null) {
            request.newBuilder()
                    .header("Authorization", accessToken)
                    .build()
        } else {
            request
        }
    }
}

现在每次发送请求时,Interceptor都会获取用户的凭据并向请求添加标头。