系统加密服务-后台解密
2018-03-03 17:02
525 查看
系统加密服务-后台解密
涉及的问题
要知道解密是否成功对于AJAX传过来的PYLOAD 载荷的流的形式的数据解密并重构可重复读取的流
要对后端透明后端不需要改动任何代码
解密通过重写HttpServletRequestWrapper 实现
构建可重复读取的的request流需要 spring-test jar支持使用DelegatingServletInputStream 实现构建ParameterRequestWrapper
public class ParameterRequestWrapper extends HttpServletRequestWrapper { private static final Logger logger = LoggerFactory.getLogger(ParameterRequestWrapper.class); private Map<String, String[]> parameters = new LinkedHashMap<String, String[]>(); //是否可重复读取流 private boolean isReadInputStream = false; //pyload parameter 主体 private String parameterBody = null; //解密状态 private boolean decryptionState = false; /** * input stream 的buffer * */ public ParameterRequestWrapper(HttpServletRequest request) throws UnsupportedEncodingException { super(request); //request 解密 RequestEnriry requestEnriry = ParameterUtils.decrypt(request); if (null != requestEnriry) { //获取解密后的对象 Map<String, String[]> parameterMap = requestEnriry.getParameterMap(); //流是否被读取了 isReadInputStream = requestEnriry.isReadInputStream(); if (isReadInputStream) { parameterBody = requestEnriry.getParameterBody(); } //解密是否成功 decryptionState = requestEnriry.isPass(); if (null != parameterMap && !parameterMap.isEmpty()) { parameters = parameterMap; } } } @Override public String getParameter(String key) { String[] values = parameters.get(key); return StringUtils.arrayToString(values); } @Override public Map<String, String[]> getParameterMap() { return parameters; } @Override public Enumeration<String> getParameterNames() { return new Vector<String>(parameters.keySet()).elements(); } @Override public String[] getParameterValues(String name) { String[] result = null; Object value = parameters.get(name); if (value == null) { result = null; } else if (value instanceof String[]) { result = (String[]) value; } else if (value instanceof String) { result = new String[]{(String) value}; } else { result = new String[]{value.toString()}; } return result; } @Override public ServletInputStream getInputStream() throws IOException { if (isReadInputStream) { if (null != parameterBody) { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(parameterBody.getBytes()); //构建可重复读取的流 return new DelegatingServletInputStream(byteArrayInputStream); } } else { return super.getInputStream(); } return null; } public boolean isDecryptionState() { return decryptionState; } public void setDecryptionState(boolean decryptionState) { this.decryptionState = decryptionState; }
构建filter
public class ParametersFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //强制指定编码,解决解密后乱码问题 request.setCharacterEncoding("UTF-8"); ParameterRequestWrapper parameterRequestWrapper = new ParameterRequestWrapper(request); //获取加密状态 boolean isDecryptionState = parameterRequestWrapper.isDecryptionState(); if (isDecryptionState) { chain.doFilter(parameterRequestWrapper, response); } else { //返回加密失败的状态,可以在页面处理 response.setStatus(911); } } }
在web.xml 设置filter
需要RequestContextListener 支持,在web.xml 中配置
!-- 参数过滤器 --> <filter> <filter-name>ParametersFilter</filter-name> <filter-class>com.xxx.common.security.web.filter.ParametersFilter</filter-class> </filter> <filter-mapping> <filter-name>ParametersFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- request固化器 --> <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
这样就配置完了
对于普通的parameterMap参数解密
先检查参数名称是否是加密的key 我们可以指定一个不容易重名的例如“@@.ecryptedData“如果加密了 就就行解密 解密完成后通过fastJson将JSON串转换为MAP
检查是否存在我们JS中定义的时间戳 如果不存在 则判断解密失败
代码片段如下
参数解密
public void decrypt(HttpServletRequest request, RequestEnriry requestEnriry) { //检查是否是form表单提交的 if (check(request)) { Map<String, String[]> parameterMap = requestEnriry.getParameterMap(); //检查是否加密 boolean isEncrypt = isEncrypt(parameterMap); if (isEncrypt) { requestEnriry.setEncrypt(isEncrypt); //获取加密的参数 String encParameter = getEncryptedParameter(parameterMap); //解密数据 String decParameter = decryptData(encParameter, getSecretKey(request)); if (StringUtils.isNotEmpty(decParameter)) { //参数转换 Map<String, String[]> decParameterMap = encParameterConver(decParameter); //将参数封装到实体中 requestEnriry.putParameterMap(decParameterMap); //设置传过来的时间戳 requestEnriry.setTimestamp(getEncryptedTimestamp(decParameterMap)); } } } }
检查参数是否加密
public boolean isEncrypt(Map parameterMap) { Map<String, String[]> parameterMap = requestEnriry.getParameterMap(); if (null != parameterMap && !parameterMap.isEmpty()) { if (null != parameterMap && !parameterMap.isEmpty() && parameterMap.containsKey("$@$.ecryptedData")) { flag = true; } } return flag; }
获取加密的参数
public String getEncryptedParameter(Map<String, String[]> parameterMap) { String ecryptedParam = null; if (null != parameterMap && !parameterMap.isEmpty()) { String[] parameterArray = parameterMap.get("$@$.ecryptedData"); if (null != parameterArray && parameterArray.length > 0) { ecryptedParam = parameterArray[0]; } } return ecryptedParam; }
检查是否需要解密操作
public boolean check(HttpServletRequest request) { Map parameterMap = request.getParameterMap(); if (null != parameterMap && !parameterMap.isEmpty()) { return true; } return false; }
参数转换
public Map<String, String[]> encParameterConver(String decryptionJson) { Map<String, String[]> mappingMap = new LinkedHashMap<String, String[]>(); if (null != mappingMap && StringUtils.isNotEmpty(decryptionJson)) { Map<String, String[]> parameterMap = null; parameterMap = ParameterUtils.jsonToMap(decryptionJson); if (null != parameterMap && !parameterMap.isEmpty()) { Set<String> keys = parameterMap.keySet(); for (String key : keys) { if (StringUtils.isNotEmpty(key)) { String[] value = parameterMap.get(key); if (null != value) { value = ParameterUtils.decodeURI(value); } if (null != value) { mappingMap.put(key, value); } } } } } return mappingMap; }
获取时间戳
public String getEncryptedTimestamp(Map<String, String[]> parameterMap) { String timestamp = null; if (null != parameterMap && !parameterMap.isEmpty()) { String[] valueArray = parameterMap.get("$@$.tmp"); if (null != valueArray && valueArray.length > 0) { timestamp = valueArray[0]; } } return timestamp; }
对于AJAX PYLOAD 载荷的参数解密
跟普通的一样解密一样只是有几点区别pyload需要有contentType
contentType 不能包含multipart/form-data 即不支持文件上传
pyload 需要吧解析的参数还原为原始的字符串 可能是JSON字符串或者是URL参数
代码片段如下
参数解密
public void decrypt(HttpServletRequest request, RequestEnriry requestEnriry) { //检查是否需要解密 if (check(request)) { //获取pyload 参数 String pyloadParameter = getPyloadParameter(request); //设置流读取状态为true requestEnriry.setReadInputStream(true); if (StringUtils.isNotEmpty(pyloadParameter)) { requestEnriry.setParameterBody(pyloadParameter); //将pyload参数解析 Map<String, String[]> parameterMap = ParameterUtils.getUrlParams(pyloadParameter); //检查是否加密 boolean isEncrypt = isEncrypt(parameterMap); if (isEncrypt) { requestEnriry.setEncrypt(isEncrypt); String encParameter = getEncryptedParameter(parameterMap); if (StringUtils.isNotEmpty(encParameter)) { String decParameter = decryptData(encParameter, getSecretKey(request)); requestEnriry.setParameterBody(decParameter); Map<String, String[]> map = ParameterUtils.jsonToMap(decParameter); if (null != map && !map.isEmpty()) { requestEnriry.setTimestamp(getEncryptedTimestamp(map)); requestEnriry.putParameterMap(map); } } } } } }
检查是否是pyload形式
public boolean check(HttpServletRequest request) { String contentType = getContentType(request); if (StringUtils.isNotEmpty(contentType) && !contentType.contains("multipart/form-data")) { return true; } return false; }
获取pyload 参数
public String getPyloadParameter(HttpServletRequest request) { String ecryptedParam = null; InputStream inputStream = null; try { inputStream = request.getInputStream(); } catch (IOException e) { logger.error("Error reading the request body…", e); } if (null != inputStream) { StringBuilder stringBuilder = new StringBuilder(); if (inputStream != null) { try { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); char[] charBuffer = new char[CHAR_BUFFER_LENGTH]; int bytesRead; while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { stringBuilder.append(charBuffer, BUFFER_START_POSITION, bytesRead); } } catch (IOException e) { logger.error("Fail to read input stream", e); } } else { stringBuilder.append(""); } ecryptedParam = stringBuilder.toString(); ac41 } return ecryptedParam; }
其他公共类 RequestEnriry
public class RequestEnriry { private Map<String, String[]> parameterMap = new HashMap<String, String[]>(); private String parameterBody; private boolean isEncrypt = false; private boolean isReadInputStream = false; private String timestamp = null; public RequestEnriry() { } public RequestEnriry(Map<String, String[]> requestParameterMap) { if (null != requestParameterMap && !requestParameterMap.isEmpty()) { parameterMap.putAll(requestParameterMap); } } public void handle() { parameterMap.remove(SecurityConstant.ECRYPTED_PARAM_NAME); } public boolean isPass() { boolean isPass = false; if (isEncrypt) { if (StringUtils.isNotEmpty(timestamp)) { isPass = true; } } else { isPass = true; } return isPass; } public Map<String, String[]> getParameterMap() { return parameterMap; } public void setParameterMap(Map<String, String[]> parameterMap) { this.parameterMap = parameterMap; } public void putParameterMap(Map<String, String[]> subParameterMap) { parameterMap.putAll(subParameterMap); } public String getParameterBody() { return parameterBody; } public void setParameterBody(String parameterBody) { this.parameterBody = parameterBody; } public boolean isEncrypt() { return isEncrypt; } public void setEncrypt(boolean encrypt) { isEncrypt = encrypt; } public String getTimestamp() { return timestamp; } public void setTimestamp(String timestamp) { this.timestamp = timestamp; } public boolean isReadInputStream() { return isReadInputStream; } public void setReadInputStream(boolean readInputStream) { isReadInputStream = readInputStream; } }
到这一步已经全部完成了,核心思想和代码已经完成
下一篇讲下碰到的问题以及总结
相关文章推荐
- 【求助,已经解决】未能注册sharepoint服务。已引发类型为SYSTEM.ARGUMENTEXCEPTION异常,其他异常信息,加密和解密过程中出错,系统错误代码为997
- jquery对中文进行base64加密,后台用java进行base64解密
- 跟后台打印程序系统服务通讯时出现错误的解决方法
- 字符串CryptoJS前台加密,pycrypto后台解密(备份一下)
- VC实现系统热键激活后台服务程序
- 项目升级-加密的参数传递到后台然后解密(相当于重新封装下request)
- QQ18年,解密8亿月活的QQ后台服务接口隔离技术
- centos6.4系统对root用户,分区,grub加密解密
- Service后台服务、前台服务、IntentService、跨进程服务、无障碍服务、系统服务
- 关于 Des加密(Android与ios 与后台java服务器之间的加密解密)
- VC++启动或停止指定的系统后台服务
- 设置scrapyd为系统后台服务及系统启动项
- js前台加密 java后台解密(des)
- 日志(跟后台打印程序系统服务通讯时出现错误)解决办法
- 网络数据通信加密系统中加密解密流程
- 简述Android系统前台进程、可见进程、服务进程、后台进程和空进程的优先级排序原因。
- URL请求对参数前端JS加密,后台JAVA解密
- ios客户端RSA公钥加密 .net后台私钥解密解决方案(基于Openssl)
- VC++启动或停止指定的系统后台服务
- ios下使用RSA算法加密与java后台解密配合demo