OkHttp全局刷新token

OkHttp全局刷新token

前言:公司项目采用token验证,要求token失效后,能够自动刷新,并且如果有其他网络请求,能够用这个刷新后的token继续请求数据。
知识介绍:token分为access_token和refresh_token,access_token有效期为2个小时,refresh_token有效期为15天。access_token失效后,需要用refresh_token进行刷新。关于token机制可以看文章基于 Token 的身份验证。
解决方法:
1.通过拦截器,获取返回的数据
2.判断token是否过期
3.如果token过期则刷新token
4.使用最新的token,重新请求网络数据
5.关于重复请求token的问题

1、新建token拦截器,获取返回的数据

public class TokenInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(requestBuilder.build());
        if (isTokenExpired(response)) {//根据和服务端的约定判断token过期
        String newToken = getNewToken();
        if (!TextUtils.isEmpty(newToken)){
            //使用新的Token,创建新的请求
             Request newRequest = chain.request()
                        .newBuilder()
                        .removeHeader(AppConstant.AUTH_HEADER)
                        .addHeader(AppConstant.AUTH_HEADER, "bearer "+newToken)
                        .addHeader("Accept","application/json;version=1.2")
                        .build();
                //重新请求
                return chain.proceed(newRequest);
        } else {
            //退出app到登录页面,重新登录
        }
    }
 }

2、根据与后台商定的token过期码,判断是否token过期

private boolean isTokenExpired(Response response) {
        if(response.code() == 401) {
            ResponseBody body = response.body();
            if (body != null){
                try {
                    MediaType mediaType = body.contentType();
                    if (mediaType != null) {
                        if(isText(mediaType)) {
                            String resp = body.string();
                            PostOkModel model = new Gson().fromJson(resp, PostOkModel.class);
                            //4061是与后台商量的token失效后的错误码,具体应根据自己的项目决定
                            if(model.getCode() == 4601) {
                                return true;
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    private boolean isText(MediaType mediaType)
    {
        if (mediaType.type() != null && mediaType.type().equals("text"))
        {
            return true;
        }
        if (mediaType.subtype() != null)
        {
            if (mediaType.subtype().equals("json") ||
                    mediaType.subtype().equals("xml") ||
                    mediaType.subtype().equals("html") ||
                    mediaType.subtype().equals("webviewhtml")
                    )
                return true;
        }
        return false;
    }

3、token过期则进行刷新token

private static String getNewToken() throws IOException{
        String refreshToken = SPDtadUtils.getString(UserApplication.getInstance(), "refreshToken");//之前存储在本地的refreshToken
       TokenRefreshPostModel model = new TokenRefreshPostModel();
        model.refresh_token = refreshToken;
        model.client_id = BuildConfig.CLIENT_ID;//项目中的身份id,根据自己实际情况定
        RequestBody body = RequestBody.create(AppConstant.MEDIA_TYPE_JSON, new Gson().toJson(model));
        Request request = new Request.Builder().url(AppConstant.TOKEN_REFRESH).post(body).build();
        Response newResponse = new OkHttpClient().newCall(request).execute();
        Log.e("TAG", "刷新token");
        if(newResponse.code() != 200) {
           return null;
        }
        ResponseBody responseBody = newResponse.body();
        //以下代码为将token存储到本地
        TokenInfoModel tokenInfoModel = new Gson().fromJson(responseBody.string(), TokenInfoModel.class);
        UserUtils.saveToken(UserApplication.getInstance(), tokenInfoModel);
        SPDtadUtils.putString(UserApplication.getInstance(), "new_token", tokenInfoModel.getAccess_token());
        Log.e("TAG", "存储token");
        //返回刷新后的token
        return tokenInfoModel.getAccess_token();
    }

4、使用最新的token,重新进行网络请求

//使用新的Token,创建新的请求
             Request newRequest = chain.request()
                        .newBuilder()
                        .removeHeader(AppConstant.AUTH_HEADER)
                        .addHeader(AppConstant.AUTH_HEADER, "bearer "+newToken)
                        .addHeader("Accept","application/json;version=1.2")
                        .build();
                //重新请求
                return chain.proceed(newRequest);

5、关于重复请求token的问题

每一个网络请求都有一个自己的拦截器,那如何实现当一个网络请求正在刷新token的时候,其他网路请求需要等待;然后等token刷新后,其他的网路请求再使用这个刷新后的token呢。这时候就要用到java中的同步锁。关于同步锁的具体内容,请查看相关的知识。
加上同步锁以后的最终代码就是一下:

public class TokenInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        String tokenHeader = chain.request().header(ApiConstants.AUTH_HEADER_VALUE);
        GlobalDoctorData user = UserApplication.getInstance().getDoctorData();
        Request.Builder requestBuilder = chain
                .request()
                .newBuilder();
        if(TextUtils.isEmpty(tokenHeader) && user !=null && !TextUtils.isEmpty(user.getToken())) {
            requestBuilder
                    .removeHeader(AppConstant.AUTH_HEADER)
                    .addHeader(AppConstant.AUTH_HEADER, user.getToken())
                    .addHeader("Accept","application/json;version=1.2")
                    .build();
        }
        Response response = chain.proceed(requestBuilder.build());
        Log.e("TokenInteceptor", "response.code=" + response.code());
        if (isTokenExpired(response)) {//根据和服务端的约定判断token过期
            Log.e("TokenInteceptor", "静默自动刷新Token,然后重新请求数据");
            //同步请求方式,获取最新的Token
            SPDtadUtils.putString(UserApplication.getInstance(), "new_token", "");
            String newToken = getNewToken();
            if(TextUtils.isEmpty(newToken)) {
                UserApplication.getInstance().userLogout(true);
                throw new IOException(UserApplication.getInstance().getResources().getString(R.string.token_fail));
            }else {
                //使用新的Token,创建新的请求
                Request newRequest = chain.request()
                        .newBuilder()
                        .removeHeader(AppConstant.AUTH_HEADER)
                        .addHeader(AppConstant.AUTH_HEADER, "bearer "+newToken)
                        .addHeader("Accept","application/json;version=1.2")
                        .build();
                //重新请求
                return chain.proceed(newRequest);
            }
        }

        return response;
    }

    private synchronized static String getNewToken() throws IOException{
        Log.e("TAG", "执行上锁");
        String refreshToken = SPDtadUtils.getString(UserApplication.getInstance(), "refreshToken");
        if(TextUtils.isEmpty(refreshToken)) {
            return null;
        }
        String new_token = SPDtadUtils.getString(UserApplication.getInstance(), "new_token");
        if(!TextUtils.isEmpty(new_token)) {
            return new_token;
        }
        TokenRefreshPostModel model = new TokenRefreshPostModel();
        model.refresh_token = refreshToken;
        model.client_id = BuildConfig.CLIENT_ID;
        RequestBody body = RequestBody.create(AppConstant.MEDIA_TYPE_JSON, new Gson().toJson(model));
        Request request = new Request.Builder().url(AppConstant.TOKEN_REFRESH).post(body).build();
        Response newResponse = new OkHttpClient().newCall(request).execute();
        Log.e("TAG", "刷新token");
        if(newResponse.code() != 200) {
            //退出登录并返回到登录页面的逻辑
            SPDtadUtils.putString(UserApplication.getInstance(), "refreshToken", "");
            return null;
        }
        ResponseBody responseBody = newResponse.body();
        TokenInfoModel tokenInfoModel = new Gson().fromJson(responseBody.string(), TokenInfoModel.class);
        UserUtils.saveToken(UserApplication.getInstance(), tokenInfoModel);
        SPDtadUtils.putString(UserApplication.getInstance(), "new_token", tokenInfoModel.getAccess_token());
        Log.e("TAG", "存储token");
        return tokenInfoModel.getAccess_token();
    }

    private boolean isTokenExpired(Response response) {
        if(response.code() == 401) {
            ResponseBody body = response.body();
            if (body != null){
                try {
                    MediaType mediaType = body.contentType();
                    if (mediaType != null) {
                        if(isText(mediaType)) {
                            String resp = body.string();
                            PostOkModel model = new Gson().fromJson(resp, PostOkModel.class);
                            if(model.getCode() == 4601) {
                                return true;
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    private boolean isText(MediaType mediaType)
    {
        if (mediaType.type() != null && mediaType.type().equals("text"))
        {
            return true;
        }
        if (mediaType.subtype() != null)
        {
            if (mediaType.subtype().equals("json") ||
                    mediaType.subtype().equals("xml") ||
                    mediaType.subtype().equals("html") ||
                    mediaType.subtype().equals("webviewhtml")
                    )
                return true;
        }
        return false;
    }
}

总结:以上就是我在项目中用的刷新token的方法。如果有不对的地方或者可以改进的地方,麻烦请告知,谢谢!

参考博文:
http://www.jianshu.com/p/8d1ee61bc2d2
http://www.jianshu.com/p/62ab11ddacc8

你可能感兴趣的:(个人总结)