SpringMvc扩展自定义注解实现web解密 不侵入业务代码(2)
1.需求
在项目中spring web服务为外部APP提供rest接口,为了 安全考虑 一般会加入自己的验证,常规 的签名+数据加密, 当然一个好的架构师或者负责人会在项目初期会考虑到的问题现老代码一般是在@Controller层每个request的开始和结尾处做的加密解密操作,这样的代码重复度很高,而且不利于更好的利用Spring框架的HttpMessageConverter达到java丰富类型的自动转换。因为APP\传到后台是加密后的数据。无法做到自动拆箱,所以说我们要在接受参数之前进行解密操作,在返回数据的时候进行加密操作!
2.实现方式
- 拦截器实现
- 切面拦截实现
- 自定义注解实现
- 1考虑前两种的性能,以及复用性本文采用 自定义注解去实现,当然这只是其中一部分原因。
- 2是看起来比较厉害对吧。(程序员都是骚的不行滴。O(∩_∩)O)
3.具体代码
- 包命名
- 我认为一份好的代码以及要有规矩,当然我是工作不太久,但是我在尽量对我的代码和各种命令写好。
- com.xxx.open
- com.xxx.open.common.annotation -------------------- 自定义注解包
- com.xxx.common.container -------------------- 容器产量
- com.xxx.common.entity -------------------- 实体类
- com.xxx.common.exception -------------------- 自定义异常
- com.xxx.common.support -------------------- 对自定义注解算是的实现吧
- com.xxx.util -------------------- 加密解密工具类
2.自定义注解实现返回数据加密
package com.caigaoqing.tech.common.support;
import com.caigaoqing.tech.common.annotation.ResponseBodyEncrypt;
import com.caigaoqing.tech.common.entity.R;
import com.caigaoqing.tech.util.encrypt.AesCBC;
import org.springframework.core.MethodParameter;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* @Author 蔡高情
* @Description 加密响应
* @Date 12:34 2018/7/9 0009
* @Param
* @return
**/
public class ResponseBodyEncryptReturnValueHandler implements HandlerMethodReturnValueHandler {
private final HandlerMethodReturnValueHandler delegate;
//通过构造注入
public ResponseBodyEncryptReturnValueHandler(HandlerMethodReturnValueHandler delegate) {
this.delegate = delegate;
}
/**
* 判断方法上的注解是不是ResponseBodyEncrypt
* @param methodParameter
* @return
*/
@Override
public boolean supportsReturnType(MethodParameter methodParameter) {
Method method=methodParameter.getMethod();
//父类是否含有RestController
Annotation[] classAnnotation =method.getDeclaringClass().getAnnotations();
boolean flag=false;
for (int i = 0; i < classAnnotation.length; i++) {
if(classAnnotation[i].annotationType().equals(RestController.class)){
flag=true;
break;
}
}
return methodParameter.getMethodAnnotation
(ResponseBodyEncrypt.class) != null || methodParameter.getMethodAnnotation(ResponseBody.class) != null ||flag ;
}
@Override
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
//获得返回值类型
Class<?> returnParaType = returnType.getParameterType();
if (!void.class.isAssignableFrom(returnParaType)) {
boolean flag= R.class.isAssignableFrom(returnParaType);
if(flag){
R r=(R)returnValue;
Object data=r.get("data");
String result= AesCBC.encrypt(data.toString(),"1234567812345678","1234567812345678");
r.put("data",result);
returnValue=r;
}
// 不是Response、Model等类型的返回值,需要包裹为Response类型
if (!flag && ! HttpServletResponse.class.isAssignableFrom(returnParaType) &&!Model.class.isAssignableFrom(returnParaType) && returnType.getMethodAnnotation(ResponseBodyEncrypt.class) != null) {
String result= AesCBC.encrypt(returnValue.toString(),"1234567812345678","1234567812345678");
returnValue=new R(result);
}
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
}
}
把返回数据存放到spring中
package com.caigaoqing.tech.common.support;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import java.util.ArrayList;
import java.util.List;
/**
* @Author 蔡高情
* @Description //TODO Response加密
* @Date 13:05 2018/7/9 0009
* @Param
* @return
**/
public class ResponseBodyWrapFactoryBean implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter adapter;
/**
* 初始化ResponseBodyWrapFactoryBean对象之后,执行这个方法
*/
@Override
public void afterPropertiesSet() {
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers);
decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
}
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for(int i = 0;i<handlers.size();i++){
HandlerMethodReturnValueHandler handler = handlers.get(i);
if (handler instanceof RequestResponseBodyMethodProcessor) {
ResponseBodyEncryptReturnValueHandler decorator = new ResponseBodyEncryptReturnValueHandler(handler);
//用自定义的OpenApiReturnValueHandler替换掉原来的RequestResponseBodyMethodProcessor类型处理器
handlers.set(i,decorator);
break;
}
}
}
}
4.注意事项
我代码写的不好,还请大家多多见谅!!!!
这个注解对map的非常不友好。map会优先被springmvc的自带解析器有限处理。在下节在说解决这个问题!
5.结果
著作权归作者所有,转载请标志作者。2018/08/17
上面应该是接受参数 解密的
代码应该没有问题的,具体查看码云:
https://gitee.com/CaiGaoQing/open-api