Okhttp Authenticator multithreading

后端 未结 5 569
一个人的身影
一个人的身影 2020-12-18 22:11

I am using OkHttp in my android application with several async requests. All requests require a token to be sent with the header. Sometimes I need to refresh th

5条回答
  •  春和景丽
    2020-12-18 22:20

    1. Use a singleton Authenticator

    2. Make sure the method you use to manipulate the token is Synchronized

    3. Count the number of retries to prevent excessive numbers of refresh token calls

    4. Make sure the API calls to get a fresh token and the local storage transactions to save the new token in your local stores are not asynchronous. Or if you want to make them asynchronous make sure you to you token related stuff after they are completed.
    5. Check if the access token is refreshed by another thread already to avoid requesting a new access token from back-end

    Here is a sample in 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 ")
        }
    }
    

提交回复
热议问题