您的位置:首页 > 其它

请求加密,响应数据解密,过期自动刷新并且重新请求接口

2016-10-09 11:46 471 查看
此博客是参考http://blog.csdn.net/jdsjlzx/article/details/52442113

并且增加加密和解密的操作

1.先熟悉一下retrofit2.0+中的gson转换器

最开始的来看看主要的代码



转换器是在converter包里面

在GsonConverterFactory类里面

@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);
}
}


对应的加密数据,我统一在响应解析器里面做处理

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数据的更新,使用的新的可以进行加密,再次发送请求;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐