Android OkHttp, refresh expired token

后端 未结 5 1143
我寻月下人不归
我寻月下人不归 2021-01-30 18:14

Scenario: I am using OkHttp / Retrofit to access a web service: multiple HTTP requests are sent out at the same time. At some point the auth token expires, and

5条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-30 18:56

    Thanks for your answers - they led me to the solution. I ended up using a ConditionVariable lock and an AtomicBoolean. Here's how you can achieve this: read through the comments.

    /**
     * This class has two tasks:
     * 1) sign requests with the auth token, when available
     * 2) try to refresh a new token
     */
    public class SignedRequestInterceptor implements Interceptor {
    
        // these two static variables serve for the pattern to refresh a token
        private final static ConditionVariable LOCK = new ConditionVariable(true);
        private static final AtomicBoolean mIsRefreshing = new AtomicBoolean(false);
    
        ...
    
        @Override
        public Response intercept(@NonNull Chain chain) throws IOException {
            Request request = chain.request();
    
            // 1. sign this request
            ....
    
            // 2. proceed with the request
            Response response = chain.proceed(request);
    
            // 3. check the response: have we got a 401?
            if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
    
                if (!TextUtils.isEmpty(token)) {
                    /*
                    *  Because we send out multiple HTTP requests in parallel, they might all list a 401 at the same time.
                    *  Only one of them should refresh the token, because otherwise we'd refresh the same token multiple times
                    *  and that is bad. Therefore we have these two static objects, a ConditionVariable and a boolean. The
                    *  first thread that gets here closes the ConditionVariable and changes the boolean flag.
                    */
                    if (mIsRefreshing.compareAndSet(false, true)) {
                        LOCK.close();
    
                        // we're the first here. let's refresh this token.
                        // it looks like our token isn't valid anymore.
                        mAccountManager.invalidateAuthToken(AuthConsts.ACCOUNT_TYPE, token);
    
                        // do we have an access token to refresh?
                        String refreshToken = mAccountManager.getUserData(account, HorshaAuthenticator.KEY_REFRESH_TOKEN);
    
                        if (!TextUtils.isEmpty(refreshToken)) {
                            .... // refresh token
                        }
                        LOCK.open();
                        mIsRefreshing.set(false);
                    } else {
                        // Another thread is refreshing the token for us, let's wait for it.
                        boolean conditionOpened = LOCK.block(REFRESH_WAIT_TIMEOUT);
    
                        // If the next check is false, it means that the timeout expired, that is - the refresh
                        // stuff has failed. The thread in charge of refreshing the token has taken care of
                        // redirecting the user to the login activity.
                        if (conditionOpened) {
    
                            // another thread has refreshed this for us! thanks!
                            ....
                            // sign the request with the new token and proceed
    
                            // return the outcome of the newly signed request
                            response = chain.proceed(newRequest);
                        }
                    }
                }
            }
    
            // check if still unauthorized (i.e. refresh failed)
            if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
                ... // clean your access token and prompt user for login again.
            }
    
            // returning the response to the original request
            return response;
        }
    }
    

提交回复
热议问题