請求加密,響應數據解密,過期自動刷新并且重新請求接口

此博客是參考http://blog.csdn.net/jdsjlzx/article/details/52442113并且增加加密和解密的操作

1.先熟悉一下retrofit2.0+中的gson轉換器最開始的來看看主要的代碼

主要代碼

轉換器是在converter包里面在GsonConverterFactory類里面
<pre>
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
Type newType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { type };
}

        @Override
        public Type getOwnerType() {
            return null;
        }

        @Override
        public Type getRawType() {
            return BaseResponse.class;
        }
    };
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(newType));
    if (String.class.equals(type)) {
        //響應如果是加密數據
        return new StringConverterFactory<>(adapter);
    }else{
        //響應是json對象
        return new GsonResponseBodyConverter<>(gson, adapter);
    }
}

</pre>

對應的加密數據,我統一在響應解析器里面做處理

public class StringConverterFactory<T> implements Converter<ResponseBody, Object> {
    private final TypeAdapter<T> adapter;

    StringConverterFactory(TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public Object convert(ResponseBody value) throws IOException {
        try {
            String aseKey= UserCache.getAesSecretKey();
            LogUtils.d("解密秘鑰aseKey = " + aseKey);
            String data = null;
            try {
                data = AES.dencrypt(value.string(), aseKey);
                LogUtils.e("返回數據:"+data);
                BaseResponse baseResponse = new Gson().fromJson(data, BaseResponse.class);
                if(!StringUtils.isEmpty(baseResponse.getNewAesKey())){
                    //保證AESkey在緩存中
                    LogUtils.i("獲得新的key====="+baseResponse.getNewAesKey());
                    //緩存中獲取秘鑰
                    UserCache.saveAesSecretKey(baseResponse.getNewAesKey());
                }
                if (baseResponse.isSuccess()) {
                    //保存Token
                    if(!StringUtils.isEmpty(baseResponse.getToken())){
                        LogUtils.i("保存token====="+baseResponse.getToken());
                        UserCache.saveToken(baseResponse.getToken());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            BaseResponse apiModel = (BaseResponse) adapter.fromJson(data);
            if (apiModel.getErrCode() == ErrorCode.TOKEN_NOT_EXIST) {
                throw new TokenNotExistException();
            } else if (apiModel.getErrCode() == ErrorCode.TOKEN_INVALID) {
                //token失效保存原先的key
                UserCache.saveFirstAesSecretKey(aseKey);
                throw new TokenInvalidException();
            } else if (!apiModel.isSuccess()) {
                return null;
            } else if (apiModel.isSuccess()) {
                return apiModel.data;
            }else{
                BaseResponse baseResponse = new Gson().fromJson(value.string(), BaseResponse.class);
                return baseResponse;
            }
        } finally {
            value.close();
        }
    }
}

這里面做了解密操作,每次請求都會查看是否有新的key以及token,如果存在就進行緩存操作,還有就是對返回碼做了一定的處理,后天返回10000代表token失效,直接向上面拋異常TokenInvalidException()

2.動態代理InvocationHandler方法,最關鍵的類

重試機制使用的是Rxjava中的retryWhen操作符

public class ProxyHandler implements InvocationHandler {
    private final static String JSON = "json";
    private Throwable mRefreshTokenError = null;
    //是否需要刷新token
    private boolean mIsTokenNeedRefresh;

    private Object mProxyObject;
    private String encrypt;

    public ProxyHandler(Object proxyObject) {
        mProxyObject = proxyObject;
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        return Observable.just(null).flatMap(new Func1<Object, Observable<?>>() {
            @Override
            public Observable<?> call(Object o) {
                try {
                    try {
                        if (mIsTokenNeedRefresh) {
                            //刷新token請求
                            updateMethodToken(method, args);
                        }
                        //首次請求
                        return (Observable<?>) method.invoke(mProxyObject, args);
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
            @Override
            public Observable<?> call(Observable<? extends Throwable> observable) {
                return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                    @Override
                    public Observable<?> call(Throwable throwable) {
                        //獲得從上一個請求帶過來的異常信息
                        if (throwable instanceof TokenInvalidException) {
                            //token過期
                            return refreshTokenWhenTokenInvalid();
                        } else if (throwable instanceof TokenNotExistException) {
                            //token不存在,目前沒做處理
                            return Observable.error(throwable);
                        }
                        //其他異常
                        return Observable.error(throwable);
                    }
                });
            }
        });
    }

    /**
     * 請求token數據
     * @return Observable
     */
    private Observable<?> refreshTokenWhenTokenInvalid() {
        synchronized (ProxyHandler.class) {
                UserApi.updateToken(AppContext.getContext(), "1", new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        mRefreshTokenError = e;
                    }

                    @Override
                    public void onNext(String s) {
                        //設置標志位為true
                        mIsTokenNeedRefresh = true;
                    }
                });
                if (mRefreshTokenError != null) {
                    //直接拋異常
                    return Observable.error(mRefreshTokenError);
                } else {
                    //無異常,重新請求上一個請求
                    return Observable.just(true);
                }
        }
    }

    /**
     * 更新token,并且做了加密解密操作
     */
    private void updateMethodToken(Method method, Object[] args) {
        if (mIsTokenNeedRefresh && !TextUtils.isEmpty(UserCache.getToken())) {
            Annotation[][] annotationsArray = method.getParameterAnnotations();
            Annotation[] annotations;
            if (annotationsArray != null && annotationsArray.length > 0) {
                for (int i = 0; i < annotationsArray.length; i++) {
                    annotations = annotationsArray[i];
                    for (Annotation annotation : annotations) {
                        if (annotation instanceof Query) {
                            String json = ((Query) annotation).value();
                            if (JSON.equals(json)) {
                                //替換新的token
                                LogUtils.d("替換新的token+++json="+args[i].toString());
                                //1.先解密數據 緩存中獲取秘鑰
                                String firstAesKey = UserCache.getFirstAesSecretKey();
                                try {
                                    LogUtils.d("第二次請求中解密使用首次請求的秘鑰aseKey = " + firstAesKey);
                                    String response= AES.dencrypt(args[i].toString(), firstAesKey);
                                    LogUtils.e("舊的json="+response);
                                    BaseRequest baseResponse = new Gson().fromJson(response, BaseRequest.class);
                                    baseResponse.setToken(UserCache.getToken());
                                    String newJson = new Gson().toJson(baseResponse);
                                    String aseKey= UserCache.getAesSecretKey();
                                    LogUtils.d("加密的秘鑰aseKey = " + aseKey);
                                    UserCache.saveFirstAesSecretKey(aseKey);
                                    LogUtils.e("newJson="+newJson);
                                    encrypt = AES.encrypt(newJson, aseKey);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                args[i] = encrypt;
                            }
                        }
                    }
                }
            }
            mIsTokenNeedRefresh = false;
        }
    }
}

注釋寫的很詳細了整個請求邏輯是這樣的(每次請求都會有加密和解密操作)1. 首次請求中,正常的請求,返回數據解密后發現token過期,拋出TokenInvalidException;2. 攔截token過期異常,同步請求獲取新的token,在響應轉換器里面做了token和key值的保存,最后返回一個just(true),并把標志位設置為true,重試請求原先的Observable;3. 判斷標志位,更新token,在updateMethodToken()方法中,使用第一次的加密key,解密參數,然后做token數據的更新,使用的新的可以進行加密,再次發送請求;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 所謂,君子性非異也,善假于物也!~ 那么,本文意在給大家提供快速、全面、高效的面試解決方案; 為大家節約尋找面試、...
    騎小豬看流星閱讀 4,108評論 13 100
  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,252評論 1 23
  • 最近公司用到RSA數據加密傳輸,本人也只會使用,并不知其原理,剛好今天在csdn看到一位大牛的博客寫得很到位,遂搬...
    爸比好酷閱讀 1,428評論 0 1
  • 昨天,廣州恒大淘寶俱樂部官方宣布與隊史傳奇球星穆里奇正式簽約,穆里奇以自由身正式回歸,合同為期六個月,在合同期滿后...
    秦嘉卉閱讀 210評論 0 0