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

Posted

技术标签:

【中文标题】在改造并行网络调用中处理后台刷新令牌调用【英文标题】:Handle Background Refresh token call in Retrofit parallel network calls 【发布时间】:2018-01-06 19:29:14 【问题描述】:

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

【问题讨论】:

你找到答案了吗?我也面临同样的问题。 @Ajay Beniwal 你找到答案了吗? 通过同步代码块可以解决问题。 medium.com/bazaar-tech/… 【参考方案1】:

OkHttp 将自动向 Authenticator 请求凭据,当响应为 401 Not Authorized 重试上次失败的请求时。

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;
    

以与拦截器相同的方式将 Authenticator 附加到 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

【讨论】:

这不是问题的答案。该问题要求并行请求的问题! 这不是答案。问题是关于并行服务调用。我也面临同样的问题。【参考方案2】:

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

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();                
        
    

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

【讨论】:

【参考方案3】:

为避免竞争条件,您可以使用 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, expiration-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()


否则,您可能必须如上所述为刷新令牌操作创建队列。

【讨论】:

以上是关于在改造并行网络调用中处理后台刷新令牌调用的主要内容,如果未能解决你的问题,请参考以下文章

通过在改造中透明地发送另一个请求来处理特定错误

RestKit 网络限制在并行请求运行时阻止其他调用

如何在客户端处理访问令牌和刷新令牌

在 AngularJS SPA 中使用刷新令牌

何时在 DRF 中的 jwt 中调用 gettoken、刷新令牌和验证令牌?

如何在ios中实现刷新令牌