注入 Koin 的 OkHttpClient 失去授权 header
OkHttpClient injected with Koin loses Authorization header
我正在将 DI 添加到现有项目,在此过程中我遇到了 header 授权从请求中消失的问题。 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 客户端和 Retrofit 模块如下:
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()
}
当用户使用 phone 开始登录并使用“admin”授权 header 发送短信和请求时,应用程序开始使用“admin”用户并在共享首选项中保存一些凭据,当用户从短信输入代码时,他的新用户凭据将保存在共享首选项中。在该应用程序发送两个请求后,授权 header 未出现在其中。我在 Chuck 上看到了,我什至用 Charles 重新检查了它。
为了解决这个问题,我尝试了几种解决方案。首先,我将 http 客户端的注入从 single
更改为 factory
,但没有用。其次,我google了一下这个问题,没有提到这个现象。第三,我根据 this 文章写了 AccessTokenInterceptor
并且还用日志覆盖了所有内容。我注意到拦截器在正常情况下工作正常,但是当缺少授权 header 时,方法 intercept
不会被调用。这可能是默认 headerInterceptor
也不起作用的原因。第四,我升级了Retrofit和OkHttp的版本,这也没有帮助。
我注意到关于那个错误的有趣事情:如果我在 Retrofit 失去授权后重新启动应用程序 header,应用程序工作正常测试用户使用正确的令牌正确登录。任何不重新启动应用程序而重新登录的尝试都会失败。也许有人有类似的问题或知道这里发生了什么,欢迎任何想法。
我终于找到了解决这个问题的方法。问题是用户凭据在创建时仅传递给 provideClient
一次。在那一刻,用户以管理员身份登录,标准用户凭据为空,因此 ApiService
的 http 客户端是在未经授权 header.
的情况下创建的
为了解决这个问题,我更改了 AccessTokenInterceptor 形式 article(HttpClientType
是 select 的枚举,需要使用凭据):
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
获取用户的凭据并将 header 添加到请求中。
我正在将 DI 添加到现有项目,在此过程中我遇到了 header 授权从请求中消失的问题。 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 客户端和 Retrofit 模块如下:
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()
}
当用户使用 phone 开始登录并使用“admin”授权 header 发送短信和请求时,应用程序开始使用“admin”用户并在共享首选项中保存一些凭据,当用户从短信输入代码时,他的新用户凭据将保存在共享首选项中。在该应用程序发送两个请求后,授权 header 未出现在其中。我在 Chuck 上看到了,我什至用 Charles 重新检查了它。
为了解决这个问题,我尝试了几种解决方案。首先,我将 http 客户端的注入从 single
更改为 factory
,但没有用。其次,我google了一下这个问题,没有提到这个现象。第三,我根据 this 文章写了 AccessTokenInterceptor
并且还用日志覆盖了所有内容。我注意到拦截器在正常情况下工作正常,但是当缺少授权 header 时,方法 intercept
不会被调用。这可能是默认 headerInterceptor
也不起作用的原因。第四,我升级了Retrofit和OkHttp的版本,这也没有帮助。
我注意到关于那个错误的有趣事情:如果我在 Retrofit 失去授权后重新启动应用程序 header,应用程序工作正常测试用户使用正确的令牌正确登录。任何不重新启动应用程序而重新登录的尝试都会失败。也许有人有类似的问题或知道这里发生了什么,欢迎任何想法。
我终于找到了解决这个问题的方法。问题是用户凭据在创建时仅传递给 provideClient
一次。在那一刻,用户以管理员身份登录,标准用户凭据为空,因此 ApiService
的 http 客户端是在未经授权 header.
为了解决这个问题,我更改了 AccessTokenInterceptor 形式 article(HttpClientType
是 select 的枚举,需要使用凭据):
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
获取用户的凭据并将 header 添加到请求中。