android - okhttp 身份验证器请求重试不起作用
您好,我们正在身份验证系统中使用 okhttp/retrofit
和 oauth2
,实际上,当 api 端点返回 UNAUTHORIZED 时,会触发 Authenticator
,我们成功获取新凭据,但问题是身份验证器没有重试先前返回 UNAUTHORIZED 响应的端点, 这就是我所做的:
@Singleton
class RefreshTokenAuthenticator @Inject constructor(
@Named(CURRENT_CREDENTIALS_PREFERENCES_NAME)
private val userDataPreferences: SharedPreferences
) : okhttp3.Authenticator {
var apiHolder: ApiHolder? = null
private val json = Json { ignoreUnknownKeys = true }
override fun authenticate(route: Route?, response: Response): Request? {
val credentialsString = userDataPreferences.getString(
USER_CREDENTIALS_KEY, USER_CREDENTIALS_KEY
) ?: ""
var request: Request? = null
val credentials: UserCredentials? = try {
json.decodeFromString(
UserCredentials.serializer(), credentialsString
)
} catch (e: SerializationException) {
null
}
credentials?.let { userCredentials ->
CoroutineScope(Dispatchers.IO).launch {
try {
val refreshTokenResponse = apiHolder?.api?.refreshToken(userCredentials)
if (refreshTokenResponse?.isSuccessful == true) {
val responseBody = refreshTokenResponse.body()?.data
responseBody?.let { body ->
val userCredentialsJson = json.encodeToString(
UserCredentials.serializer(),
body
)
userDataPreferences.edit().putString(
USER_CREDENTIALS_KEY,
userCredentialsJson
).apply()
request = response.request
.newBuilder()
.header("Authorization", "Bearer ${body.accessToken}")
.build()
}
} else {
request = null
//TODO: handle errors
}
} catch (e: Exception) {
Timber.e(e)
}
}
}
return request
}
}
这就是我创建 OkhttpClient 的方式:
////
return OkHttpClient.Builder()
.addInterceptor(defaultHeadersInterceptor)
.addInterceptor(authorizationInterceptor)
.addInterceptor(errorHandlingInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(CurlInterceptor {
Timber.d("Ok2Curl $it ")
})
.authenticator(refreshTokenAuthenticator)
.build()
注意:如果需要,授权拦截器会在每个请求中添加承载令牌。
Hello we're working with okhttp/retrofit
and oauth2
in the authentication system, actually the Authenticator
is triggered when an api endpoint returns UNAUTHORIZED and we get the new credentials successfully, but the problem is the authenticator not retrying the previously endpoint that returned UNAUTHORIZED in response,
this is what i did:
@Singleton
class RefreshTokenAuthenticator @Inject constructor(
@Named(CURRENT_CREDENTIALS_PREFERENCES_NAME)
private val userDataPreferences: SharedPreferences
) : okhttp3.Authenticator {
var apiHolder: ApiHolder? = null
private val json = Json { ignoreUnknownKeys = true }
override fun authenticate(route: Route?, response: Response): Request? {
val credentialsString = userDataPreferences.getString(
USER_CREDENTIALS_KEY, USER_CREDENTIALS_KEY
) ?: ""
var request: Request? = null
val credentials: UserCredentials? = try {
json.decodeFromString(
UserCredentials.serializer(), credentialsString
)
} catch (e: SerializationException) {
null
}
credentials?.let { userCredentials ->
CoroutineScope(Dispatchers.IO).launch {
try {
val refreshTokenResponse = apiHolder?.api?.refreshToken(userCredentials)
if (refreshTokenResponse?.isSuccessful == true) {
val responseBody = refreshTokenResponse.body()?.data
responseBody?.let { body ->
val userCredentialsJson = json.encodeToString(
UserCredentials.serializer(),
body
)
userDataPreferences.edit().putString(
USER_CREDENTIALS_KEY,
userCredentialsJson
).apply()
request = response.request
.newBuilder()
.header("Authorization", "Bearer ${body.accessToken}")
.build()
}
} else {
request = null
//TODO: handle errors
}
} catch (e: Exception) {
Timber.e(e)
}
}
}
return request
}
}
and this is how i create the OkhttpClient:
////
return OkHttpClient.Builder()
.addInterceptor(defaultHeadersInterceptor)
.addInterceptor(authorizationInterceptor)
.addInterceptor(errorHandlingInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(CurlInterceptor {
Timber.d("Ok2Curl $it ")
})
.authenticator(refreshTokenAuthenticator)
.build()
NB: the authorization Interceptor adds the Bearer token in every request if needed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
看起来您需要分解 API 调用和重试代码,并且拦截器机制可能不会执行您想要的操作。
我的 API Client 显示了一种替代方法,并包装了 okhttp,以便 Android 视图模型通过单行程序进行 API 调用,而不是使用 okhttp 直接地。
Looks like you need to factor out the API calling and retrying code, and that the interceptor mechanism may not do what you want.
My API Client shows an alternative approach, and wraps okhttp, so that Android view models make API calls via a one liner, rather than using okhttp directly.
正如加里·阿彻(Gary Archer)提到的,你可以做些什么来完成未经授权的请求的重试策略,只需添加另一个拦截器,就像你已经对之前的拦截器所做的那样,并让它们在一段时间后重复你的调用 - 这完全取决于你做什么重试策略看起来像这样,但我可以给你一个可以做什么的小例子:
这里是使用协程完成的,但你可以将其调整为你的异步实现。
快乐编码!
As Gary Archer did mention, what you can do to accomplish the retry policy for your unathorized requests, just add another Interceptor as you have done with the previous ones already and make them repeat your call after a while - it is totally up to you what the retry policy will look like, but I can give you an small example of what can be done:
This one here is done with the Coroutines, but you can shape it to your async implementation.
Happy coding!