在Retrofit并行网络调用中处理后台刷新令牌调用

时间:2017-07-31 09:37:28

标签: android asynchronous retrofit

我是Android编程和Retrofit的新手,我正在制作一个示例应用程序,我必须使用访问令牌进行两个并行网络调用。 当访问令牌过期并返回401状态代码时出现问题,如果我看到401 HTTP状态代码我必须使用此访问令牌调用刷新令牌,但并行调用的问题是它会导致竞争条件以刷新刷新令牌,有没有最好的方法来避免这种情况,以及如何智能地刷新令牌而不会发生任何冲突。

3 个答案:

答案 0 :(得分:2)

当响应 401 未授权重试上次失败的请求时,OkHttp会自动向身份验证器询问凭据。

public class TokenAuthenticator implements Authenticator {
    @Override
    public Request authenticate(Proxy proxy, Response response) throws IOException {
        // Refresh your access_token using a synchronous api request
        newAccessToken = service.refreshToken();

        // Add new header to rejected request and retry it
        return response.request().newBuilder()
            .header(AUTHORIZATION, newAccessToken)
            .build();
    }

    @Override
    public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
        // Null indicates no attempt to authenticate.
        return null;
    }

身份验证器附加到 OkHttpClient ,方法与拦截器相同

OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setAuthenticator(authAuthenticator);

创建Retrofit RestAdapter时使用此客户端

RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(ENDPOINT)
            .setClient(new OkClient(okHttpClient))
            .build();
return restAdapter.create(API.class);

请检查:Fore more details visit this link

答案 1 :(得分:0)

尝试为刷新令牌操作创建队列,如:

class TokenProcessor {
    private List<Listener> queue = new List<Listener>();
    private final Object synch = new Object();
    private State state = State.None;
    private String token;
    private long tokenExpirationDate;

    public void getNewToken(Listener listener){
        synchronized(synch) {

            // check token expiration date
            if (isTokenValid()){
                listener.onSuccess(token);
                return;
            }
            queue.add(listener);

            if (state != State.Working) {
                sendRefreshTokenRequest();
            }
        }
    }
    private void sendRefreshTokenRequest(){
        // get token from your API using Retrofit
        // on the response call onRefreshTokenLoaded() method with the token and expiration date
    }
    private void onRefreshTokenLoaded(String token, long expirationDate){
        synchronized(synch){
            this.token = token;
            this.tokenExpirationDate = expirationDate;

            for(Listener listener : queue){
                 try {
                   listener.onTokenRefreshed(token);
                 } catch (Throwable){}
            }
            queue.clear();                
        }
    }
}

这是一个示例代码,它是如何实现的。

答案 2 :(得分:0)

为了避免竞争条件,您可以使用 ReentrantLock 同步刷新令牌代码。例如,如果请求 A 和请求 B 同时尝试刷新令牌,由于代码是同步的,因此刷新 A 会实际刷新令牌。一旦完成,请求 B 将运行 refreshToken() 并且应该有一些逻辑告诉请求 B 令牌已经刷新。一个例子可能是存储令牌刷新发生的时间戳,然后检查令牌是否在过去 10 秒内被刷新。

val lock = ReentrantLock(true)

fun refreshToken(): Boolean {
     lock.lock()
     if (token has been refreshed in last 10 seconds): return true
     api.refresh()
     lock.unlock()

}

如果您不想使用最后 10 秒的逻辑,这里有另一种方法。每当您刷新令牌时,后端都会返回 {accessToken, expire-timestamp}。现在,请求 A 将这个令牌和过期时间保存在磁盘中。请求 B 只需要使用时间戳检查以确保令牌没有过期。如果请求 B 得到 401 并且令牌没有过期,则表示请求 A 刷新了令牌。示例代码:

val lock = ReentrantLock(true)

fun refreshToken(): Boolean {
     lock.lock()
     if (token has not expired): return true
     api.refresh()
     lock.unlock()

}

否则,您可能需要为上面提到的刷新令牌操作创建一个队列。