OkHttp全局刷新token

来源:互联网 发布:大数据架构师做什么 编辑:程序博客网 时间:2024/06/11 02:52

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

原创粉丝点击