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。
流程圖如下:
在這裏插入圖片描述
但上面的流程有一個需要注意的地方,就是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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章