服务端API请求现在基本上都会设置token,用来保证接口识别安全和验证,一般来说token都会设置一个有效的时间,当时间过期之后我们需要重新获取一个新的token,从而保证接口的安全性。
那么,当token有效时间过期后我们应该怎样处理了,有两种方案,分别是手动处理和自动处理,选择哪种方案就看看应用适合哪种场景。

手动处理

手动处理意味着当token过期后需要客户手动点击重新获取一个新的token,常规做法是当token过期后,应用直接跳转至登录界面,重新走一遍登录的流程,获取新的token,更新本地缓存,从而保证后续接口的正常请求操作。

首先,怎么判断接口token失效,我的接口请求是采用Retrofit+Okhttp框架,需要在Retrofit请求中配置拦截,如图代码示例

retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request newRequest = chain.request().newBuilder()
                                .addHeader("Authorization", UserHelpers.getInstance().getAuthorization())
                                .build();
                        Response proceed = chain.proceed(newRequest);
                        /*token过期重新登录*/
                        if(isTokenExpired(proceed))
                        loginAgain();
                        else
                            isTokenExpire=false;
                        return proceed;
                    }
                })
                .addInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
                .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)
                .build()).baseUrl(RetrofitWrapper.Constant.BASE_URL).addConverterFactory(
                GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();

在新增的 addInterceptor中配置回调结果的拦截,如果出现token异常则走对应的流程进行重新登录。

isTokenExpired根据回调接口判断token是否过期

private boolean isTokenExpired(Response response) {
        try {
            ResponseBody responseBody = response.body();
            BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.getBuffer();
            Charset UTF8 = Charset.forName("UTF-8");
            String string = buffer.clone().readString(UTF8);
            ResponseEntity responseEntity = ResponseWrapper.getResponseEntity(string);
            if(responseEntity.getCode()==MSG_CODE_TOKEN_FAIL)
                return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

loginAgain()重新跳转登录界面

private void loginAgain()
   {
           Activity activity = ScreenManager.getScreenManager().currentActivity();
           if (activity != null) {
               activity.startActivity(new Intent(activity, LoginActivity.class)
                       .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
               ScreenManager.getScreenManager().popAllActivity();
           }
   }

这里有很多种实现的方法,目的就是正常跳转登录界面即可

自动处理

自动处理的方案原理就是当token判断失效后,马上获取一个新的token保证接口能正常请求完成

retrofit = new Retrofit.Builder().client(new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request newRequest = chain.request().newBuilder()
                                .addHeader("Authorization", UserHelpers.getInstance().getAuthorization())
                                .build();
                        Response proceed = chain.proceed(newRequest);
//                        /*token过期重新登录*/
//                        if(isTokenExpired(proceed))
//                        loginAgain();
//                        else
//                            isTokenExpire=false;
                        if(isTokenExpired(proceed)) {
                            String newToken = getNewToken();
                            //使用新的Token,创建新的请求
                            Request newAgainRequest = chain.request().newBuilder()
                                    .addHeader("Authorization", newToken)
                                    .build();
                            return chain.proceed(newAgainRequest);
                        }
                        return proceed;
                    }
                })
                .addInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .readTimeout(TIMEOUT_READ, TimeUnit.SECONDS)
                .connectTimeout(TIMEOUT_CONNECTION, TimeUnit.SECONDS)
                .build()).baseUrl(RetrofitWrapper.Constant.BASE_URL).addConverterFactory(
                GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();

如图当token过期后,我们马上获取一个新的token从而继续完成整个接口请求。

getNewToken()获取一个新的token

private String getNewToken() throws IOException {
        Retrofit retrofit= new Retrofit.Builder()
                /*API的主机地址*/
                .baseUrl("https://url")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        retrofit2.Response<JsonObject> requestToken = retrofit.create(ApiProtocol.class).DriverLogin().execute();
        /*生成新的token*/
        String token=requestToken.body().get("Token").toString();
        /*需要更新本地的token保存*/
        return token;
    }

并发处理

很多时候我们可能存在多个接口并发请求的情况,这个时候如果token失效,我们需要做一些特殊的处理,如果手动解决方案,我们参考下面的解决方案
在跳转的时候加一个token过期标识判断,当第一个接口已经判断token过期后,后续的的无需在处理
/token过期重新登录/

if(isTokenExpired(proceed))
   loginAgain();
  else
   isTokenExpire=false;
   
 private void loginAgain()
   {
       /*处理并发请求过期判断*/
          if(isTokenExpire)
           return;
          else
           isTokenExpire=true;
           Activity activity = ScreenManager.getScreenManager().currentActivity();
           if (activity != null) {
               activity.startActivity(new Intent(activity, LoginActivity.class)
                       .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK));
               ScreenManager.getScreenManager().popAllActivity();
           }
   }

如果是自动解决方案则在处理刷新的token的方法中需要采用同步的方式,这样第一个接口处理刷新完token后,后续的接口都可以正常请求接口。

@Synchronized
    private String getNewToken() throws IOException {
        Retrofit retrofit= new Retrofit.Builder()
                /*API的主机地址*/
                .baseUrl("https://url")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        retrofit2.Response<JsonObject> requestToken = retrofit.create(ApiProtocol.class).DriverLogin().execute();
        /*生成新的token*/
        String token=requestToken.body().get("Token").toString();
        /*需要更新本地的token保存*/
        return token;
    }

关于token过期的处理方案的分享就到这里,如果你还有更好的方案,欢迎留言分享