您的位置:首页 > 编程语言 > Java开发

系统加密服务-后台解密

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;
}
}


到这一步已经全部完成了,核心思想和代码已经完成

下一篇讲下碰到的问题以及总结
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  加解密 java pyload 重构