Okhttp Authenticator多线程

hex*_*ons 8 authentication multithreading android okhttp

OkHttp在我的Android应用程序中使用了几个异步请求.所有请求都需要使用标头发送令牌.有时我需要使用RefreshToken刷新令牌,所以我决定使用OkHttp's Authenticatorclass.

当两个或多个异步请求同时从服务器获得401响应代码时会发生什么?authenticate()是为每个请求调用Authenticator的方法,还是只为第一个获得401的请求调用一次?

@Override
public Request authenticate(Proxy proxy, Response response) throws IOException
{                
    return null;
}
Run Code Online (Sandbox Code Playgroud)

如何只刷新一次令牌?

Tar*_*tar 12

  1. 使用单例 Authenticator

  2. 确保您用来操作令牌的方法是 Synchronized

  3. 统计重试次数,防止刷新令牌调用次数过多

  4. 确保用于获取新令牌的 API 调用和用于在本地存储中保存新令牌的本地存储事务不是异步的。或者,如果您想让它们异步,请确保在它们完成后标记相关的东西。
  5. 检查访问令牌是否已被另一个线程刷新以避免从后端请求新的访问令牌

这是 Kotlin 中的示例

@SingleTon
class TokenAuthenticator @Inject constructor(
    private val tokenRepository: TokenRepository
) : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        return if (isRequestRequiresAuth(response)) {
            val request = response.request()
            authenticateRequestUsingFreshAccessToken(request, retryCount(request) + 1)
        } else {
            null
        }
    }

    private fun retryCount(request: Request): Int =
        request.header("RetryCount")?.toInt() ?: 0

    @Synchronized
    private fun authenticateRequestUsingFreshAccessToken(
        request: Request,
        retryCount: Int
    ): Request? {
        if (retryCount > 2) return null

        tokenRepository.getAccessToken()?.let { lastSavedAccessToken ->
            val accessTokenOfRequest = request.header("Authorization") // Some string manipulation needed here to get the token if you have a Bearer token

            if (accessTokenOfRequest != lastSavedAccessToken) {
                return getNewRequest(request, retryCount, lastSavedAccessToken)
            }
        }

        tokenRepository.getFreshAccessToken()?.let { freshAccessToken ->
            return getNewRequest(request, retryCount, freshAccessToken)
        }

        return null
    }

    private fun getNewRequest(request: Request, retryCount: Int, accessToken: String): Request {
        return request.newBuilder()
            .header("Authorization", "Bearer " + accessToken)
            .header("RetryCount", "$retryCount")
            .build()
    }

    private fun isRequestRequiresAuth(response: Response): Boolean {
        val header = response.request().header("Authorization")
        return header != null && header.startsWith("Bearer ")
    }
}
Run Code Online (Sandbox Code Playgroud)