系统加密服务-后台解密

系统加密服务-后台解密

个人博客:https://www.baiyp.ren

涉及的问题

  • 要知道解密是否成功
  • 对于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();
        }
        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;
    }
}

到这一步已经全部完成了,核心思想和代码已经完成
下一篇讲下碰到的问题以及总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值