android okhttp更新token方案

参考文章:
(五星推荐)retrofit+rxjava 刷新token并发处理 https://www.jianshu.com/p/eb7042693d52
retrofit 刷新token并发处理 https://www.jianshu.com/p/c325f5c32709
Android上使用retrofit+okhttp时token失效的处理方案 https://www.jianshu.com/p/62ab11ddacc8
OkHttp 更新 token 的解决方案 https://juejin.im/entry/584144d0128fe1006c3cfb8f
如何改变OkHttp Response中的主体?https://androidcookie.com/okhttp-response.html

最近项目中为了实现token过期增加会话保持的概念,引入accessToken及refreshToken机制,增加对用户的管理,accessToken一般过期时间较短,accessToken过期后,使用refreshToken获取新的accessToken,refreshToken也有过期时间,refreshToken过期后需要退出登录,引导用户重新登录。

所有接口请求的header中需要加入accessToken字段,而当accessToken过期时,会接收到服务端返回的特定错误码,此时使用refreshToken调用刷新token的接口获取新的accessToken并保存,之后的接口请求就携带新的accessToken;而当使用refreshToken调用刷新token时,返回refreshToken过期时,则需要退出登录,引导用户重新登录,重新获取refreshToken/accessToken。
流程图如下:
android okhttp更新token方案_第1张图片
但上面的流程有一个需要注意的地方,就是accessToken过期时。
假如在进入app主页时会同时请求好几个接口,那这几个接口都会返回accessToken过期,每个接口获取到accessToken过期,都去调用一次刷新token的接口是不合理的,应该是第一次拦截到accessToken过期时,调用刷新token接口并加锁,当获取到新的token保存到本地,并且之前等待的几个接口请求替换各自参数中的accessToken使用最新获得的accessToken发起重试。

再然后就是talk is cheap, show your code环节了

OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor( new Interceptor() {
                @Override
                public Response intercept(@NonNull Chain chain) throws IOException {
                    Request.Builder builder = chain.request().newBuilder();
                  	...
                    String oldAccessToken = "";
                    if (mAccessToken != null) {
                        //获取令牌
                        builder.addHeader("Authorization", mAccessToken);
                        oldAccessToken = mAccessToken;
                    }
                    RequestBody body = chain.request().body();
                   ...
                    //处理accessToken失效
                    Response response = chain.proceed(builder.build());
                    if (response != null) {
                        ResponseBody responseBody = response.body();
                        if (responseBody != null) {
                            BufferedSource source = responseBody.source();
                            source.request(Integer.MAX_VALUE);
                            Buffer buffer = source.buffer();
                            Charset charset = UTF_8;
                            MediaType contentType = responseBody.contentType();
                            if (contentType != null) {
                                charset = contentType.charset(UTF_8);
                            }
                            String bodyString = buffer.clone().readString(charset);
                            try {
                                JSONObject jsonObject = new JSONObject(bodyString);
                                String code = jsonObject.optString("code");
                                synchronized (HttpClientHelper.class) {
                                    if (TextUtils.equals(CommonConstant.HttpResultCode.ACCESS_TOKEN_INVALID, code)) {
                                        //比较请求的token与本地存储的token   如果不一致 直接重试
                                        if (!TextUtils.isEmpty(oldAccessToken) && !TextUtils.isEmpty(mAccessToken)
                                                && !TextUtils.equals(mAccessToken, oldAccessToken)) {
                                            builder.header("Authorization", mAccessToken)
                                                    .build();
                                            return chain.proceed(builder.build());
                                        }
                                        if (mContext != null) {
                                            SPEditor spEditor = SPEditor.getInstance(mContext.getApplicationContext());
                                            String refreshToken = spEditor.loadStringInfo(CommonConstant.SPKey.REFRESH_TOKEN);
                                            if (!TextUtils.isEmpty(refreshToken)) {
                                                RefreshTokenResponse data = service.refreshToken(refreshToken).execute().body();
                                                if (data != null) {
                                                //刷新token失败 重写原接口的请求 在原接口请求回调中处理了退出登录
                                                    if (!TextUtils.equals(CommonConstant.HttpResultCode.SUCCESS, data.getCode()) || data.getData() == null || TextUtils.isEmpty(data.getData().getAccessToken())) {
                                                        jsonObject.put("code", data.getCode());
                                                        jsonObject.put("msg", data.getMsg());
                                                        return response.newBuilder().body(ResponseBody.create(contentType, jsonObject.toString())).build();
                                                    } else {
                                                        String accessToken = data.getData().getAccessToken();
                                                        spEditor.saveStringInfo(CommonConstant.SPKey.ACCESS_TOKEN, accessToken);
                                                        mAccessToken = accessToken;
                                                        String reTryCurrentTimeMillis = String.valueOf(System.currentTimeMillis());
                                                        builder.header("Authorization", mAccessToken)
                                                                .build();
                                                        return chain.proceed(builder.build());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    return response;
                }
            };)
                .writeTimeout(20L, TimeUnit.SECONDS)
                .readTimeout(20L, TimeUnit.SECONDS)
                .addInterceptor(logging);

你可能感兴趣的:(android知识点)