系统加密服务-后台解密
涉及的问题
要知道解密是否成功
对于AJAX传过来的PYLOAD 载荷的流的形式的数据解密并重构可重复读取的流
要对后端透明后端不需要改动任何代码
解密通过重写HttpServletRequestWrapper 实现
构建可重复读取的的request流需要 spring-test jar支持使用DelegatingServletInputStream 实现
构建ParameterRequestWrapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91public 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;
}
}
}
public String getParameter(String key) {
String[] values = parameters.get(key);
return StringUtils.arrayToString(values);
}
public Map<String, String[]> getParameterMap() {
return parameters;
}
public Enumeration<String> getParameterNames() {
return new Vector<String>(parameters.keySet()).elements();
}
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;
}
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class ParametersFilter extends OncePerRequestFilter {
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 中配置
1 | !-- 参数过滤器 --> |
这样就配置完了
对于普通的parameterMap参数解密
- 先检查参数名称是否是加密的key 我们可以指定一个不容易重名的例如“$@$.ecryptedData“
- 如果加密了 就就行解密 解密完成后通过fastJson将JSON串转换为MAP
- 检查是否存在我们JS中定义的时间戳 如果不存在 则判断解密失败
代码片段如下
参数解密1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public 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));
}
}
}
}检查参数是否加密
1
2
3
4
5
6
7
8
9public 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;
}获取加密的参数
1
2
3
4
5
6
7
8
9
10public 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;
}
检查是否需要解密操作
1 | public boolean check(HttpServletRequest request) { |
参数转换
1 | public Map<String, String[]> encParameterConver(String decryptionJson) { |
获取时间戳
1 | public String getEncryptedTimestamp(Map<String, String[]> parameterMap) { |
对于AJAX PYLOAD 载荷的参数解密
- 跟普通的一样解密一样只是有几点区别
- pyload需要有contentType
- contentType 不能包含multipart/form-data 即不支持文件上传
- pyload 需要吧解析的参数还原为原始的字符串 可能是JSON字符串或者是URL参数
代码片段如下
参数解密
1 | public void decrypt(HttpServletRequest request, RequestEnriry requestEnriry) { |
检查是否是pyload形式
1 | public boolean check(HttpServletRequest request) { |
获取pyload 参数
1 | public String getPyloadParameter(HttpServletRequest request) { |
其他公共类 RequestEnriry
1 | public class RequestEnriry { |
到这一步已经全部完成了,核心思想和代码已经完成