需求背景

最近项目组中接到新的需求,要求app访问服务端的接口中,需要在报文传输中对某个字段进行加密,同时若返回响应中有该字段,则需要加密返回app;为了兼容旧版本app,与客户端同事商议,在新版本中该字段传输格式为:加密文本+特定的分割符+加密方式。

解决思路

  • 服务端通过传入字段判断,如果是“加密字段+特殊分割符+加密方式”这个方式拼接的,则需要对加密文本通过相对应的解密方式解密;
  • app涉及的服务特别多,接口也非常多,对每个接口进行判断并解密操作,显然是不靠谱的,不但工作量大而且容易出错;
  • 能否实现一个公共模块,对请求参数进行统一处理,让请求参数到达业务接口层时,已经是解密好的数据。

解决方法

  • 由于项目框架是spring+ Jersey 1.x,查资料jersery看是否有对应的filter处理这种情况,果不其然Jersey的ContainerRequestFilter可以实现该功能,参考:http://codehustler.org/blog/jersey-cross-site-scripting-xss-filter-for-java-web-apps/,具体的实现代码如下:
/*** 加密方式常量类*/
public class EncryptContant {public static  final String ENCRYPT_BY_BASE64 = "BASE64";public static  final String ENCRYPT_BY_MD5 = "MD5";
}
public class RequestParamFilter implements ContainerRequestFilter{@Overridepublic ContainerRequest filter( ContainerRequest request ){// Clean the query stringscleanParams( request.getQueryParameters() );cleanParams( request.getFormParameters() );// Return the cleansed requestreturn request;}private void cleanParams( MultivaluedMap<String, String> parameters ){for( Map.Entry<String, List<String>> params : parameters.entrySet() ){String key = params.getKey();List<String> values = params.getValue();List<String> cleanValues = new ArrayList<String>();String value = values.get(0);if (value.split("_").length == 2 && EncryptContant.ENCRYPT_BY_BASE64.equeals(value.split("_")[1])) {value = Base64Utils.decryptStr(value.split("_")[0]));}cleanValues.add(value);parameters.put( key, cleanValues );}}

然后在web.xml配置下Filter,就可以对请求参数进行过滤解密了,然而这种方式是有局限性的,对于post请求提交的参数没法解析,也就是对jersey@FormParam这种不行,通过提供的方法request.getFormParameters()获取不到参数,只能解决@QueryParam这种参数,或许jersery2中已经实现了,但由于整个项目是jersery1的,升级框架版本是不可能的,除非以后对单个项目进行重构;

  • 考虑采用新的解决方案,通过springAop,扫描所有接口方法,获取参数列表,对参数进行判断解密,但是在参数列表中可能参数是@Context HttpServletRequest类型,且业务代码中有部分数据是从这个request中获取的,因此还需要对Request的所有参数判断并解密,由于J2EE的Request的数据是没法改变的,因此只能重写Request,并将重写的request做为新的参数传入接口方法;
    具体的实现方法如下,示例中用"_"作为特殊分割符:
@Aspect   //定义一个切面
@Configuration
@Order(3)
public class RequestParamDealAspect {// 定义切点Pointcut@Pointcut("execution(* com.*.controller.*Controller.*(..))")public void excudeService() {}@Around("excudeService()")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {Object [] args = pjp.getArgs();Object [] newArgs = new Object[args.length];for (int i=0;i<args.length;i++) {Object item = args[i];if (args[i] instanceof HttpServletRequest) {item = new MyHttpServletRequestWrapper((HttpServletRequest) item);}if(args[i] instanceof  String){if (item != null && args[i].toString().split("_").length == 2 && EncryptContant.ENCRYPT_BY_BASE64.equeals(item.toString().split("_")[1])) {item = Base64Utils.decryptStr(item.toString().split("_")[0]);}}newArgs[i] = item;}Object result = pjp.proceed(newArgs);return result;}
}
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {public HttpServletRequest originalRequest;public Map decryptParameterMap;public MyHttpServletRequestWrapper(HttpServletRequest request) {super(request);originalRequest = request;decryptParameterMap = new HashMap();Map<String, String[]> properties = request.getParameterMap();Map<String, String> returnMap = new HashMap<String, String>();Iterator<Map.Entry<String, String[]>> entries = properties.entrySet().iterator();Map.Entry<String, String[]> entry;String key = "";String value = "";while(entries.hasNext()){entry = entries.next();key =  entry.getKey();Object valueObj = entry.getValue();if (null == valueObj) {value = "";} else if (valueObj instanceof String[]) {String[] values = (String[]) valueObj;value = values[0];if(value.split("_").length == 2  && EncryptContant.ENCRYPT_BY_BASE64.equals(value.split("_")[1])){value = Base64Utils.decryptStr(value.split("_")[0]);}} else {value = valueObj.toString();}returnMap.put(key, value);}decryptParameterMap.putAll(returnMap);}@Overridepublic String getParameter(String s){// 返回解密后的参数return String.valueOf(decryptParameterMap.get(s));}@Overridepublic Enumeration getParameterNames(){// 这里是通过实体类注入参数return Collections.enumeration(decryptParameterMap.keySet());}@Overridepublic String[] getParameterValues(String s){// 这里是注入参数Object o = decryptParameterMap.get(s);if (o == null){return null;} else{return new String[] {String.valueOf(o)};}}@Overridepublic Map getParameterMap(){return decryptParameterMap;}
}

通过这种方式对任何方式的传参均可以进行加密解析

  • 业务中通过具体的加密方式对响应字段进行加密,通过定义filter和ThreadLocal在对request参数解析,保存加密字段和对应的加密方式的映射关系,然后在业务代码中获取要加密字段的加密方式;
public class EncryptThreadLocal {public static ThreadLocal<Map<String,String>> threadLocalEncrypt = new ThreadLocal<Map<String,String>>();
}
public class EncryptFilter implements Filter {public void destroy() {EncryptThreadLocal.threadLocalEncrypt.remove();}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;Map<String, String[]> parameterMap = httpRequest.getParameterMap();Iterator<Map.Entry<String, String[]>> it = parameterMap.entrySet().iterator();Map.Entry<String, String[]> entry = null;String key;String value;Map encrytMap = new HashMap();while (it.hasNext()){entry = it.next();key =  entry.getKey();value= entry.getValue()[0];if(value.split("_").length == 2 ){encrytMap.put(key,value.split("_")[1]);}}EncryptThreadLocal.threadLocalEncrypt.set(encrytMap);try {chain.doFilter(request, response);}finally {EncryptThreadLocal.threadLocalEncrypt.remove();}}
}

获取加密方式,对字段进行加密

 Map encryptMap = EncryptThreadLocal.threadLocalEncrypt.get();String test="adafgaga";String encryptType = (String) encryptMap.get("test");if(EncryptContant.ENCRYPT_BY_BASE64.equals(encryptType )){test = ase64Utils.ecryptStr(test);}

总结

  • 通过上面的方法,完美的解决了这个需求,并且对业务代码有很小的侵入,若返回响应中没有需要的加密的字段,就不需要配置EncryptFilter;
  • 如果传入的参数值中本身就含有"_",则可以考虑先将字段escape,然后在服务端接口unescape,就不会出现问题。

通过SpringAop、Filter对Request参数解密和对Response内容加密相关推荐

  1. RSA加密-解密以及解决超长内容加密失败解决

    加解密(没有使用到证书):https://blog.csdn.net/qy20115549/article/details/83105736 生成证书网站:https://blog.csdn.net/ ...

  2. springboot中的拦截器interceptor和过滤器filter,多次获取request参数

    大家好,我是烤鸭:     这是一篇关于springboot的拦截器(interceptor)和过滤器(Filter). 先说一下过滤器和拦截器. 区别: 1. servlet请求,顺序:Filter ...

  3. 软键盘实例 request参数修改

    软键盘实例 request参数修改 这里的键盘 里面的内容是乱序的,每次请求都会不一样的顺序. 键盘内容是一个DIV的背景图,方框是DIV边框.控制按钮直接在前台控制就行. 请求流程如下 JSP请求软 ...

  4. Django框架(20.Django的视图函数的request参数以及QueryDict对象)

    HttpReqeust对象 服务器接收到http协议的请求后,会根据报文创建HttpRequest对象,这个对象不需要我们创建,直接使用服务器构造好的对象就可以.视图的第一个参数必须是HttpRequ ...

  5. servlet request参数只能取一次解决方法

    servlet request参数只能取一次解决方法 参考文章: (1)servlet request参数只能取一次解决方法 (2)https://www.cnblogs.com/go4mi/p/10 ...

  6. 基于Java Bean Validation对Request参数进行校验的设计思路

    数据校验是任何一个应用程序都会用到的功能,无论是显示层还是持久层. 通常,相同的校验逻辑会分散在各个层中, 这样,不仅浪费了时间还会导致重复代码的发生. 为了避免重复, 开发人员经常会把这些校验逻辑直 ...

  7. Google翻译参数解密(11-11)

    Google翻译参数解密(11-11) 一.请求过程 文件地址:https://translate.google.com.hk/translate/releases/twsfe_w_20201102_ ...

  8. 通过Filter给Request、Response加点料

    一.简介 使用过 ASP.NET 的同学都知道,Filter 是一个非常中的概念,是项目开发过程中也是经常使用的.今天我们就来讲讲,通过Filter给 Request.Response 加点料.通过 ...

  9. thinkphp6 request参数读取

    thinkphp6 request参数读取,原来是这么读取的,研究了好久 //dump($request); //dump($request->rule());  //dump($request ...

  10. Thymeleaf+SpringMVC,在html中获取数据session、request参数的值

    Request参数 假设请求的url为:/user/get?id=12 1.访问参数id可以使用param前缀,例如:${param.id!=null}检查是否有参数id: 2.参数是一个数组,因为它 ...

最新文章

  1. 美国能源部宣布SunShot2020目标完成超90%
  2. 关于js封装函数的一些东西
  3. 《图谋职场——最经济的图形沟通》 专题讲座圆满成功
  4. 4.0 编译apk中无classes.dex问题解决方法
  5. 【SpringMVC入门】SpringMVC环境搭建、接收参数的几种方式、视图解析器、@ResponseBody
  6. Java 8 Friday Goodies:本地交易范围
  7. 回归分析预测_使用回归分析预测心脏病。
  8. 【POJ - 1523】SPF(Tarjan求割点,求分割成的连通块数,模板题,tricks)
  9. web 前端 如何分享到instagram_如何找到靠谱的Web培训机构?web前端培训机构哪个好?...
  10. C#LeetCode刷题之#111-二叉树的最小深度​​​​​​​(Minimum Depth of Binary Tree)
  11. python 石头剪刀布,Python石头剪刀布完整代码
  12. [Flink]Flink 的物理分区器
  13. Android显示shp文件,Arcgis for Android移动平台能直接打开本地的shp文件吗
  14. 春节挣一波快钱,Top 19 接私活平台
  15. jscc控制器说明书_JSCC精研调速器 精研SF90E数显调速器 90W精研马达面板控制器
  16. php发布iis0x80070005,iis 0x80070005 解决方法
  17. 安装和配置Oracle10g详细教程
  18. 头条号想过新手,但指数却一直达不到650,该怎么过新手?
  19. 名校认证证书、免费课程……留学生不会告诉你的10个宝藏自学网站
  20. python的“end=”介绍

热门文章

  1. 服务器linux命令aux,Linux查看所有进程ps -aux命令介绍
  2. 多路复用器_多路复用、非阻塞、线程与协程
  3. echarts 获取点击的y轴数值_ECharts关系图
  4. 从输入 URL 到页面展示,这中间发生了什么?
  5. Cesium:添加按钮与原生按钮样式相同
  6. 论文笔记_S2D.52_CMRNet++:在激光雷达地图中进行内参未知的相机的单目视觉定位
  7. 论文笔记_S2D.45_ORBSLAM-Atlas: 一个稳健和精确的多建图系统
  8. 朴素贝叶斯(naive Bayes)原理与应用
  9. 限时删!我亲自整理一套目标检测、卷积神经网络和OpenCV学习资料(教程/PPT/代码)...
  10. 自动化测试中依据元素实时截图,比较元素图片是否一致(selenium和appium通用)...