我正在将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标头后重新启动应用程序,则该应用程序可以正常运行,并且使用正确的令牌正确记录了测试用户。在不重新启动应用程序的情况下进行任何尝试重新登录都会失败。也许有人遇到类似的问题,或者知道这里发生了什么,欢迎任何想法。
答案 0 :(得分:0)
我终于找到了解决这个问题的方法。问题是用户凭据在创建时仅传递给provideClient
一次。此时,用户以admin身份登录,并且标准用户凭据为空,因此ApiService
的http客户端被创建为没有Authorization标头。
为解决此问题,我更改了AccessTokenInterceptor表单article(HttpClientType
是一个枚举,用于选择需要使用的凭据):
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
都会获取用户的凭据并向请求添加标头。