首页 > 解决方案 > 在改造 android 中使用身份验证器刷新访问令牌

问题描述

如何使用身份验证器刷新我的令牌?当我在我的 api 调用中得到 401 时,我需要刷新令牌方法来返回令牌或 null。

class SupportInterceptor() : Interceptor, Authenticator {
    /**
     * Interceptor class for setting of the headers for every request
     */
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request = request?.newBuilder()
            ?.addHeader("Content-Type", "application/json")
            ?.addHeader("app-id", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
            ?.build()
        return chain.proceed(request)
    }

    /**
     * Returns a request that includes a credential to satisfy an authentication challenge in
     * [response]. Returns null if the challenge cannot be satisfied.
     *
     * The route is best effort, it currently may not always be provided even when logically
     * available. It may also not be provided when an authenticator is re-used manually in an
     * application interceptor, such as when implementing client-specific retries.
     */
    override fun authenticate(route: Route?, response: Response): Request? {
        var requestAvailable: Request? = null
        try {

            return runBlocking {
                when (val tokenResponse = refreshToken()) {
                    is Success -> {
                        //                        userPreferences.saveAccessTokens(
                        //                            tokenResponse.value.access_token!!,
                        //                            tokenResponse.value.refresh_token!!
                        //                        )
                        response.request.newBuilder()
                            .header("Authorization", "Bearer ${tokenResponse.value.access_token}")
                            .build()
                    }
                    else -> null
                }
            }

            //            requestAvailable = response?.request?.newBuilder()
            ////                ?.addHeader("Authorization", "Bearer $token")
            //                ?.build()
            //            return requestAvailable
        } catch (ex: Exception) {
        }
        return requestAvailable
    }


    suspend fun refreshToken(): Either<Failure, String?> {

        return withContext(Dispatchers.IO) {
            try {
                val PREFS_NAME = "userPref"
                val sharedPref: SharedPreferences =
                    context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

                val refreshToken = sharedPref.getString(MyConstants.KEY_REFRESH_TOKEN, "")

                val retrofit: Retrofit = Retrofit.Builder()
                    .baseUrl(baseURL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()

                val api: TokenRefreshApi = retrofit.create(TokenRefreshApi::class.java)

                val response = api.refreshAccessToken(refreshToken).execute()

//                val call: Call<LogIn> = api.refreshAccessToken(refreshToken)

                when (response.isSuccessful) {

                    false -> Either.Left(response.errorResponse())

                    true -> {

                        val editor: SharedPreferences.Editor = sharedPref.edit()

                        editor.putString(
                            MyConstants.KEY_ACCESS_TOKEN,
                            response.body()!!.access_token
                        )
                        editor.putString(
                            MyConstants.KEY_REFRESH_TOKEN,
                            response.body()!!.refresh_token
                        )

                        editor!!.apply()

                        response.body()!!.access_token

                    }

                }
            } catch (e: Exception) {
                Timber.e("searchTasks: $e")
                Either.Left(Failure.UnknownError)
            }

        }

    }
}

标签: androidauthenticationretrofit2interceptorrefresh-token

解决方案


我首先要澄清可靠客户的行业标准行为:

  • 客户端尝试使用访问令牌的 API 请求
  • 如果客户端收到 401,它会尝试静默刷新访问令牌并使用新令牌重试 API 请求
  • 如果存在技术问题,请避免重定向用户以重新进行身份验证

这是我的一些简单的 Kotlin 代码

在我看来,Retrofit 的 Authenticator 界面让这更容易,并且会为你重试。您的代码看起来大部分正确且与我的相似,但没有手动检查 401:

  • 不过,您需要测试 401,一种方法是在开发期间向访问令牌添加任意字符,以模拟到期

推荐阅读