请求加密,响应数据解密,过期自动刷新并且重新请求接口
来源:互联网 发布:软件开发项目经费预算 编辑:程序博客网 时间:2024/06/09 15:33
此博客是参考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数据的更新,使用的新的可以进行加密,再次发送请求;
0 0
- 请求加密,响应数据解密,过期自动刷新并且重新请求接口
- Retrofit Token过期自动刷新并重新请求接口
- angular实现页面跳转,并且刷新页面(重新请求后台接口)
- jsp中自动重新发送请求刷新当前页面
- JMeter BeanShell 的使用-请求加密-返回自动解密处理
- postman加密请求接口
- 处理HTTP响应(响应码、响应头、请求重定向、自动刷新/跳转)
- 处理HTTP响应(响应码、响应头、请求重定向、自动刷新/跳转)
- 处理HTTP响应(响应码、响应头、请求重定向、自动刷新/跳转)
- java和.net相互http请求并且经过des的cbc模式加密解密
- java和.net相互http请求并且经过des的cbc模式加密解密(转)
- 请求响应/加密解密时一个需要注意的小问题
- retrofit 自定义请求参数加密 和自定义响应解密 带你走出那些坑
- 火狐设置每次刷新重新请求
- 移动端请求URL的加密/解密
- HTTP数据请求与响应
- session过期,处理ajax请求,使其重新登录
- volley设置缓存过期后重新请求缓存
- ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
- nginx获取真实ip——toa模块
- ubuntu/linux惠普触摸屏关闭/开启
- 添加自己的网络层
- PL/SQL如何设置主键自动递增
- 请求加密,响应数据解密,过期自动刷新并且重新请求接口
- 使用java下载一首歌(HttpClient/JavaIO)
- Jenkins学习
- Highcharts使用时遇到的问题及解决方案
- 移植笔记:使用qemu模拟器运行Kylin FT1500A.iso
- Java实战总结
- MongoDB学习(三) 基本操作
- 一致性哈希算法的一种PHP实现代码(上一篇文章)
- STL使用vtk类降幂