此博客是參考http://blog.csdn.net/jdsjlzx/article/details/52442113并且增加加密和解密的操作
1.先熟悉一下retrofit2.0+中的gson轉(zhuǎn)換器最開(kāi)始的來(lái)看看主要的代碼
轉(zhuǎn)換器是在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)) {
//響應(yīng)如果是加密數(shù)據(jù)
return new StringConverterFactory<>(adapter);
}else{
//響應(yīng)是json對(duì)象
return new GsonResponseBodyConverter<>(gson, adapter);
}
}
</pre>
對(duì)應(yīng)的加密數(shù)據(jù),我統(tǒng)一在響應(yīng)解析器里面做處理
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("返回?cái)?shù)據(jù):"+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();
}
}
}
這里面做了解密操作,每次請(qǐng)求都會(huì)查看是否有新的key以及token,如果存在就進(jìn)行緩存操作,還有就是對(duì)返回碼做了一定的處理,后天返回10000代表token失效,直接向上面拋異常TokenInvalidException()
2.動(dòng)態(tài)代理InvocationHandler方法,最關(guān)鍵的類
重試機(jī)制使用的是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請(qǐng)求
updateMethodToken(method, args);
}
//首次請(qǐng)求
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) {
//獲得從上一個(gè)請(qǐng)求帶過(guò)來(lái)的異常信息
if (throwable instanceof TokenInvalidException) {
//token過(guò)期
return refreshTokenWhenTokenInvalid();
} else if (throwable instanceof TokenNotExistException) {
//token不存在,目前沒(méi)做處理
return Observable.error(throwable);
}
//其他異常
return Observable.error(throwable);
}
});
}
});
}
/**
* 請(qǐng)求token數(shù)據(jù)
* @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) {
//設(shè)置標(biāo)志位為true
mIsTokenNeedRefresh = true;
}
});
if (mRefreshTokenError != null) {
//直接拋異常
return Observable.error(mRefreshTokenError);
} else {
//無(wú)異常,重新請(qǐng)求上一個(gè)請(qǐng)求
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.先解密數(shù)據(jù) 緩存中獲取秘鑰
String firstAesKey = UserCache.getFirstAesSecretKey();
try {
LogUtils.d("第二次請(qǐng)求中解密使用首次請(qǐng)求的秘鑰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;
}
}
}
注釋寫的很詳細(xì)了整個(gè)請(qǐng)求邏輯是這樣的(每次請(qǐng)求都會(huì)有加密和解密操作)1. 首次請(qǐng)求中,正常的請(qǐng)求,返回?cái)?shù)據(jù)解密后發(fā)現(xiàn)token過(guò)期,拋出TokenInvalidException;2. 攔截token過(guò)期異常,同步請(qǐng)求獲取新的token,在響應(yīng)轉(zhuǎn)換器里面做了token和key值的保存,最后返回一個(gè)just(true),并把標(biāo)志位設(shè)置為true,重試請(qǐng)求原先的Observable;3. 判斷標(biāo)志位,更新token,在updateMethodToken()方法中,使用第一次的加密key,解密參數(shù),然后做token數(shù)據(jù)的更新,使用的新的可以進(jìn)行加密,再次發(fā)送請(qǐng)求;