0%

Spring获取Request输入流

前言

我们在进行请求进行拦截的时候经常会碰上这样一个问题,我们想要在拦截器filter中获取request的请求,如果使用请求中默认的getInputStream()方法或者getReader()方法获取数据,但是在后面的Controller中使用@ResquestBody注解,我们读取不到request的body中的值,这是因为request的body中的数据只能通过getInputStream()和getReader()方法读取一次,要解决这个问题,我们要对重写request请求的getInputStream()和getReader()方法。

获取POST请求的输入流,并修改

一般在POST请求中我们携带的信息是application/json格式的。在一些场景中我们要对这些POST请求中的application/json信息进行获取、解析。
先看代码:

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
public class XssRequestWrapper extends HttpServletRequestWrapper {

private static Policy policy = null;

private static final AntiSamy antiSamy = new AntiSamy();
/**
* 请求Body中的String类型
*/
private String body;
/**
* application/json转成的Map类型
*/
private Map<String, String> parameters = new HashMap<String, String>();
/**
* 字符串的流
*/
private byte[] bytes;
/**
* 判断是否进行了数据的初始化
*/
private boolean isInit = false;
/**
* 默认的字符串格式
*/
private static final String DEFAULT_CHARSET_NAME = "UTF-8";
private ObjectMapper objectMapper = new ObjectMapper();

public XssRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try {
String contentType = request.getContentType();
if(contentType.contains(";")){
contentType = contentType.substring(0,contentType.indexOf(";"));
}
//初始化bytes中的数据
initBytes();
//解析application/json中的内容
if(contentType.equals(MediaType.APPLICATION_JSON_VALUE)){
parseJsonParameters();
}
} catch (IOException e) {
throw new RuntimeException("IOException", e);
}
}
/**
* 第一次执行要进行数据的初始化
* @throws IOException
*/
private void initBytes() throws IOException {
isInit = true;
StringBuilder buffer = new StringBuilder();
BufferedReader reader = this.getHttpServletRequest().getReader();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
body = buffer.toString();
//获取输入流到本地
bytes = body.getBytes(DEFAULT_CHARSET_NAME);
}
/**
* 解析ApplicationJson数据,并且经过XSS过滤
* @throws IOException
*/
private void parseJsonParameters() throws IOException {
StringBuilder stringBuilder = new StringBuilder("{");
if (null != body && body.length() > 0) {
JsonNode node = objectMapper.readTree(body);
Iterator<String> fieldNames = node.getFieldNames();
for (; fieldNames.hasNext();) {
String key = fieldNames.next();
String value = node.get(key).toString();
if (value.length() > 2 && value.startsWith("\"")) {
String valueTemp = value.substring(1,value.length()-1);
valueTemp = StringEscapeUtils.unescapeJava(valueTemp);
//TODO:XSS白名单过滤

valueTemp = StringEscapeUtils.escapeJava(valueTemp);
valueTemp = "\"" + valueTemp + "\"";
parameters.put(key, valueTemp);
} else {
parameters.put(key, value);
}
}
}
for(String key:parameters.keySet()){
String value = parameters.get(key);
stringBuilder.append("\""+key+"\":"+value+",");
}
stringBuilder.deleteCharAt(stringBuilder.length()-1);
stringBuilder.append("}");
try {
bytes = stringBuilder.toString().getBytes(DEFAULT_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private HttpServletRequest getHttpServletRequest() {
return (HttpServletRequest) super.getRequest();
}
/**
* 重写getReader方法
* @return
* @throws IOException
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 重写getInputStream()方法
* 可以修改body内容
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (!isInit)
initBytes();
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
};
}

总结

分析:获取Post请求输入流,又保证数据不丢失的步骤如下:

  • 继承父类的构造方法,在自己的方法中进行数据的初始化
  • 数据的初始化就是,获取输入流,写入本类的char[] bytes中
  • 重新getInputStream()方法和getReader()方法,其中getInputStream()方法直接读取bytes字节流信息
  • 解析application/json数据,利用JsonNode类
原创技术分享,您的支持将鼓励我继续创作。