使用Retrofit进行高效令牌刷新

时间:2017-11-14 07:43:10

标签: android authentication retrofit2 access-token okhttp3

我有一个类来处理令牌过期后的令牌刷新。代码如下:

public class TokenAuthenticator implements Authenticator {

    @Nullable
    @Override
    public synchronized Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException {
        ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);
        Call<User> call = apiInterface.refreshTokens(new ClientRequest(Songa.getContext().getString(R.string.client_id),
                App.getContext().getString(R.string.client_secret),
                App.getContext().getString(R.string.grant_type), getRAGUser().getRefreshToken()));

        User ragUser = call.execute().body();

        if (ragUser != null) {
            Gson gson = new Gson();
            String user = gson.toJson(ragUser);
            PrefUtils.putString(Constants.USER, user);

            long tokenExpiryPeriod = System.currentTimeMillis() + Long.parseLong(ragUser.getExpiryPeriod());
            PrefUtils.putLong(Constants.TOKEN_EXPIRY_PERIOD, tokenExpiryPeriod);

            return response.request().newBuilder().header("Authorization", "Bearer " + ragUser.getAccessToken()).build();
        } else {
            if (responseCount(response) >= 3) {
                Log.e("TokenAuthenticator", String.valueOf(responseCount(response)));

                //we have failed 3 times; log the user out
                EventBus.getDefault().post(new LogoutEvent());

                return null;
            }
        }

        return null;
    }

    private int responseCount(Response response) {
        int result = 1;
        while ((response = response.priorResponse()) != null) {
            result++;
        }
        return result;
    }
}

我的意图是,一旦令牌过期,验证者应该在放弃并注销用户之前最多重试三次。但是,即使使用有效的令牌,下面的代码也会执行三次请求。

我一直认为Authenticator类仅在令牌过期时介入,但是从我的日志中,我可以看到每次发出新请求时都会调用它。

以下是我的Retrofit客户端的代码:

public class RestClient {

    private static final String BASE_URL = "https://my.base.url/api/v3/";
    private static String token = "Bearer " + getAccessToken();
    private static Retrofit retrofit = null;

    public RestClient() {
    }

    public static Retrofit getClient() {
        if (retrofit == null) {
            TokenAuthenticator tokenAuthenticator = new TokenAuthenticator();

            Dispatcher dispatcher = new Dispatcher();
            dispatcher.setMaxRequests(1);

            Gson gson = new GsonBuilder()
                    .setExclusionStrategies(new ExclusionStrategy() {
                        @Override
                        public boolean shouldSkipField(FieldAttributes f) {
                            return f.getDeclaringClass().equals(RealmObject.class);
                        }

                        @Override
                        public boolean shouldSkipClass(Class<?> clazz) {
                            return false;
                        }
                    }).create();

            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);

            OkHttpClient okClient = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .authenticator(tokenAuthenticator)
                    .addInterceptor(loggingInterceptor)
                    .addInterceptor(chain -> {
                        Request original = chain.request();

                        Request request = original.newBuilder()
                                .addHeader("Authorization", token)
                                .addHeader("Content-Type", "application/json")
                                .build();

                        return chain.proceed(request);
                    })
                    .addInterceptor(loggingInterceptor)
                    .dispatcher(dispatcher)
                    .build();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .client(okClient)
                    .build();
        }

        return retrofit;
    }

}

是否有更好的方法可以根据我的要求实施令牌认证;注销前重试3次?

0 个答案:

没有答案