【问题标题】:How to refresh token using okhttp authenticator with multiple request at a time如何使用 okhttp 身份验证器一次刷新多个请求的令牌
【发布时间】:2020-03-06 19:47:59
【问题描述】:

我在我的项目中使用改造网络。问题是我必须从我的第一个活动中调用 2 个请求。它工作正常,但是当访问令牌过期时,它必须刷新令牌。我已经使用 okhttp Authenticator 实现了一个调用。但是它调用了多次并且这个错误也显示出来了too many followup request 21

编辑 我更新了 TokenAuthenticator 类并添加了同步()。但它是从 if (originalRequest.header("Authorization") != null) return null 返回的。我正在关注这个答案https://stackoverflow.com/a/52513122/10243953

如果我要删除 if (originalRequest.header("Authorization") != null) return null 这一行,那么它可以工作,但在日志报告中我看到它多次调用刷新令牌。我怎样才能避免这种多次调用?

这是我的 Authenticator 类

class TokenAuthenticator : Authenticator {

private val refreshTokenGrandType = "refresh_token"
private var oldToken: String? = null
private var newToken: String? = null

override fun authenticate(route: Route?, response: Response?): Request? {
    oldToken = SharedPreferenceManager(MainApplication.applicationContext()).getToken()
    if (response == null) return null
    val originalRequest = response.request()
    if (originalRequest.header("Authorization") != null) return null
    if(!isTokenSaved()){
        synchronized(this) {
            RetrofitClient.client.create(Auth::class.java).refresh_token(
                SharedPreferenceManager(MainApplication.applicationContext()).getRefreshToken()!!,
                refreshTokenGrandType
            ).enqueue(object : Callback<Token> {
                override fun onFailure(call: Call<Token>, t: Throwable) {
                    Toast.makeText(
                        MainApplication.applicationContext(),
                        t.message,
                        Toast.LENGTH_SHORT
                    ).show()
                    Log.d("TokenAuth", t.message!!)
                }

                override fun onResponse(
                    call: Call<Token>,
                    response: retrofit2.Response<Token>
                ) {
                    if (response.isSuccessful) {
                        val body = response.body()
                        newToken = body!!.access_token
                        val refresh_token = body.refresh_token

                        SharedPreferenceManager(MainApplication.applicationContext()).accessToken(
                            newToken!!,
                            refresh_token
                        )

                    } else {
                        val error = response.errorBody()
                        Log.d("TokenAuthRes", error!!.string())
                    }
                }
            })
        }
    }
    return originalRequest
        .newBuilder()
        .header(
            "Authorization",
            "Bearer ${SharedPreferenceManager(MainApplication.applicationContext()).getToken()}"
        )
        .build()

}

fun isTokenSaved() : Boolean{
    if (newToken == null) return false
    if (oldToken.equals(newToken)) return false
    else return true
}
}

改造客户端

object RetrofitClient {
private lateinit var interceptor : Interceptor
private lateinit var okHttpClient: OkHttpClient
private var retrofit : Retrofit? = null

val client : Retrofit
    get(){
        val context : Context = MainApplication.applicationContext()
        interceptor = Interceptor { chain ->
            val url = chain.request()
                .url()
                .newBuilder()
                .build()

            val request = chain.request()
                .newBuilder()
                .addHeader("Authorization","Bearer ${SharedPreferenceManager(context).getToken()}")
                .url(url)
                .build()

            return@Interceptor chain.proceed(request)
        }

        okHttpClient = OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .addInterceptor(NoInternetInterception(context))
            .authenticator(TokenAuthenticator())
            .connectTimeout(1, TimeUnit.MINUTES)
            .build()

        if (retrofit == null){
            retrofit = Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(const.URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
        return retrofit!!
    }
 }

【问题讨论】:

  • 你能解决这个问题吗?
  • 我没有回答你的问题,但我在使用改造时遇到了类似的问题。经过一番搜索,我发现改造从原始请求的标头中删除了身份验证信息。然后检查 originalRequest.header("authorization") 是没有用的,因为它始终为空。您可以阅读以下内容:github.com/square/retrofit/issues/977 作为一种解决方法,我正在查看响应是否未经授权,如果是,我尝试获取令牌,因为显然请求到达了受保护的端点。

标签: android kotlin oauth retrofit okhttp


【解决方案1】:

在您的TokenAuthenticator 中试试这个:

override fun authenticate(route: Route, response: Response): Request? {
    val call = RetrofitClient.client.create(Auth::class.java).refresh_token(SharedPreferenceManager(MainApplication.applicationContext()).getRefreshToken()!!,refreshTokenGrandType)
    val refreshResponse = call.execute()
    if (refreshResponse.isSuccessful()) {
        //Save your new token
        return response
            .request()
            .newBuilder()
            .header(
                "Authorization",
                "Bearer ${SharedPreferenceManager(MainApplication.applicationContext()).getToken()}"
            )
            .build()
    } else return null
}

【讨论】:

  • 不,它不起作用。现在显示身份验证错误。
  • 来自主请求的 401 身份验证错误。您的答案适用于同时发送两个请求吗?它只会调用刷新令牌1次,然后在令牌刷新后再次调用两个api?
  • 根据我的回答,当一个主请求返回401时,令牌会同步刷新,并且会再次调用一个新的令牌。
  • 但我同时从我的活动中调用 2 请求,因此两者都在调用 Athenicator 以获取刷新令牌。我需要一些东西,比如它会停止第二个请求并在令牌刷新后再次调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-17
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
  • 2016-04-04
  • 2012-02-07
  • 2016-02-23
相关资源
最近更新 更多