OkHttp 和 Retrofit,用并发请求刷新令牌
Posted
技术标签:
【中文标题】OkHttp 和 Retrofit,用并发请求刷新令牌【英文标题】:OkHttp and Retrofit, refresh token with concurrent requests 【发布时间】: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。有什么优雅的解决方案可以避免这种情况吗?
谢谢!
【问题讨论】:
这可能对你有帮助,希望还为时不晚***.com/a/48518733/8187386 【参考方案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以上是关于OkHttp 和 Retrofit,用并发请求刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章
Okhttp + Retrofit @Body 请求 - 传输编码:添加了分块
OkHttp 和 Retrofit 无限 Post 请求在后台