此博客是參考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數據的更新,使用的新的可以進行加密,再次發送請求;