2

我使用 Retrofit2 作为 Android 的 HTTP 客户端。该应用程序使用访问令牌和刷新令牌与后端进行通信。

场景一:access-token 过期,api 调用失败,错误码 401,然后我使用 Authenticator 使用 refresh-token 获取一对新的 access-token 和 refresh-token 并继续使用新的 access-token对于失败的呼叫。这一切都适用于稳定的互联网连接

场景2:现在考虑一下,互联网不稳定,不断掉线。互联网在那里,由于访问令牌过期,api调用失败,应用程序使用刷新令牌进行登录调用以获取一对新的访问令牌和刷新令牌。但在我获得登录成功 200 之前,互联网就掉线了。现在互联网又回来了,我的应用程序开始再次触发 api 调用并获得 401,然后使用刷新令牌触发新的登录调用。这是问题所在,后端已更新了新的访问和刷新令牌,但由于互联网中断,应用程序从未收到它。因此,我拥有的刷新令牌不再有效(因为已经在后端创建了一对新令牌并刷新了旧令牌)

不确定我应该如何处理这种情况?这是我的身份验证器类:

public class MyAuthenticator implements Authenticator {

Context context;

public MyAuthenticator(Context context) {
    this.context = context;
}

@Nullable
@Override
public Request authenticate(Route route, Response response) throws IOException {

    Retrofit client = new Retrofit.Builder()
            .baseUrl(Manager.API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    ApiService service = client.create(ApiService.class);

    SharedPreferenceManager sharedPreferenceManager = new SharedPreferenceManager(context);
    String refreshToken = sharedPreferenceManager.getRefreshToken(context);

    AuthRequest authRequest = new AuthRequest();
    authRequest.grant_type = "refresh_token";
    authRequest.refresh_token = refreshToken;

    Call<authResponse> refreshTokenResult = service.refreshToken(authRequest);
    retrofit2.Response accessTokenResponse = refreshTokenResult.execute();
    //check if response equals 400 , mean empty response
    if (accessTokenResponse.isSuccessful()) {
        AuthResponse refreshResult  = (AuthResponse) accessTokenResponse.body();
        //save new access and refresh token
        sharedPreferenceManager.writeLoginResponse(context, refreshResult);
        // then create a new request and modify it accordingly using the new token
        return response.request().newBuilder()
                .header("Authorization", "Bearer " + refreshResult.access_token)
                .build();

    } else {
        LoginActivity_.intent(context).flags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK).start();
        return null;

    }
}

}

不知道我错过了什么以及我应该如何解决这个问题。

4

1 回答 1

0

你不能在客户端对此做任何事情。

您要做的就是一次性交付新代币,这是不可能的。相反,您应该设计至少一次交付。

您必须为这种情况准备服务器,即它也应该接受旧的刷新令牌,直到它至少收到一次新的访问令牌或刷新令牌。

在此之前,服务器无法确定客户端是否收到了新令牌。

于 2018-12-15T03:27:40.957 回答