【问题标题】:OkHttp and Retrofit, refresh token with concurrent requestsOkHttp 和 Retrofit,用并发请求刷新令牌
【发布时间】:2017-11-07 06:38:10
【问题描述】:

在我的应用程序中,我实现了 Retrofit 来调用 WebServices,并且我使用 OkHttp 来使用拦截器和身份验证器。有些请求需要token,我已经实现了Authenticator接口来处理刷新(遵循官方documentation)。但我有以下问题:有时在我的应用程序中,我必须一次调用多个请求。因此,对于其中一个,我将遇到 401 错误。

这是我的请求调用代码:

public static <S> S createServiceAuthentication(Class<S> serviceClass, boolean hasPagination) {

        final String jwt = JWT.getJWTValue(); //Get jwt value from Realm

        if (hasPagination) {
            Gson gson = new GsonBuilder().
                    registerTypeAdapter(Pagination.class, new PaginationTypeAdapter()).create();

            builder =
                    new Retrofit.Builder()
                            .baseUrl(APIConstant.API_URL)
                            .addConverterFactory(GsonConverterFactory.create(gson));
        }

        OkHttpClient.Builder httpClient =
                new OkHttpClient.Builder();

        httpClient.addInterceptor(new AuthenticationInterceptor(jwt));
        httpClient.authenticator(new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) throws IOException {
                if (responseCount(response) >= 2) {
                    // If both the original call and the call with refreshed token failed,
                    // it will probably keep failing, so don't try again.
                    return null;
                }

                if (jwt.equals(response.request().header("Authorization"))) {
                    return null; // If we already failed with these credentials, don't retry.
                }

                APIRest apiRest = createService(APIRest.class, false);
                Call<JWTResponse> call = apiRest.refreshToken(new JWTBody(jwt));
                try {
                    retrofit2.Response<JWTResponse> refreshTokenResponse = call.execute();
                    if (refreshTokenResponse.isSuccessful()) {

                        JWT.storeJwt(refreshTokenResponse.body().getJwt());

                        return response.request().newBuilder()
                                .header(CONTENT_TYPE, APPLICATION_JSON)
                                .header(ACCEPT, APPLICATION)
                                .header(AUTHORIZATION, "Bearer " + refreshTokenResponse.body().getJwt())
                                .build();
                    } else {
                        return null;
                    }
                } catch (IOException e) {
                    return null;
                }
            }
        });

        builder.client(httpClient.build());
        retrofit = builder.build();

        return retrofit.create(serviceClass);
    }

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

问题很简单,第一个请求会成功刷新令牌,但其他请求会失败,因为他们会尝试刷新已刷新的令牌。 WebService 返回错误 500。有什么优雅的解决方案可以避免这种情况吗?

谢谢!

【问题讨论】:

标签: android retrofit2 okhttp3


【解决方案1】:

如果我理解您的问题,在更新令牌时会发送一些请求,这会给您一个错误。

您可以尝试在更新令牌时阻止所有请求(使用“同步”对象),但这不会涵盖已发送请求的情况。

由于这个问题很难完全避免,也许这里的正确方法是有一个好的后备行为。例如,通过使用更新的令牌重新运行请求来处理您在令牌更新期间发出请求时收到的错误。

【讨论】:

    【解决方案2】:

    写服务。

    public class TokenService extends Service {
    
    private static final String TAG = "HelloService";
    
    private boolean isRunning = false;
    OkHttpClient client;
    JSONObject jsonObject;
    public static String URL_LOGIN = "http://server.yoursite";
    String phone_number, password;
    
    @Override
    public void onCreate() {
        Log.i(TAG, "Service onCreate");
        jsonObject = new JSONObject();
        client = new OkHttpClient();
    
        SharedPreferences pref_phone = getSharedPreferences("Full_Phone", MODE_PRIVATE);
        phone_number = pref_phone.getString("Phone", "");
    
        SharedPreferences pref_password = getSharedPreferences("User_Password", MODE_PRIVATE);
        password = pref_password.getString("Password", "");
    
        isRunning = true;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
        Log.i(TAG, "Service onStartCommand");
        try {
            jsonObject.put("phone_number", phone_number);
            jsonObject.put("password", password);
    
        } catch (JSONException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (; ; ) {
                    try {
                        Thread.sleep(1000 * 60 * 2);
                    } catch (Exception e) {
                    }
    
                    if (isRunning) {
    
                        AsyncTaskRunner myTask = new AsyncTaskRunner();
                        myTask.execute();
    
                    } else {
    
                        Log.d("CHECK__", "Check internet connection");
    
                    }
                }
            }
        }).start();
    
        return Service.START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent arg0) {
        Log.i(TAG, "Service onBind");
        return null;
    }
    
    @Override
    public void onDestroy() {
    
        isRunning = false;
        Log.i(TAG, "Service onDestroy");
    }
    
    String post(String url, JSONObject login) {
        try {
            MediaType mediaType = MediaType.parse("application/json");
            RequestBody body = RequestBody.create(mediaType, login.toString());
            okhttp3.Request request = new okhttp3.Request.Builder()
                    .url(url)
                    .post(body)
                    .build();
    
            okhttp3.Response response = client.newCall(request).execute();
    
            try {
                return response.body().string();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    
    String response;
    
    private class AsyncTaskRunner extends AsyncTask<String, String, String> {
    
        @Override
        protected void onPreExecute() {
        }
    
        @Override
        protected String doInBackground(String... params) {
    
            try {
                response = post(
                        URL_LOGIN, jsonObject);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        protected void onPostExecute(String result) {
            Log.d("---OKHTTP---", response);
        }
    }
    

    }

    【讨论】:

    • 我能理解您的回复吗?对我来说,这完全超出了主题。此外,我正在使用我说的改造,而不是 AyncTask
    猜你喜欢
    • 2017-04-21
    • 2016-11-06
    • 2017-12-15
    • 2016-01-05
    • 2017-06-17
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多