最近一个项目需要在请求中效验签名 签名的参数用到了请求的数据
如果请求的数据是 application/x-www-form-urlencoded 的格式那么没问题
但是如果请求的数据是 application/json 则用到了 @RequestBody 中的数据
但是由于 InputStream 只能取一次数据
最后导致 Controller 中取不到数据
解决方案
- 新建一个 - SignHttpServletRequestWrapper包装请求- 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- package com.sixi.enquiry.interceptor; 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import javax.servlet.ReadListener;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 /**
 * Created with IntelliJ IDEA
 *
 * @author 喵♂呜
 * Created on 2017/12/20 19:01.
 */
 public class SignHttpServletRequestWrapper extends HttpServletRequestWrapper {
 private final byte[] body;
 /**
 * Constructs a request object wrapping the given request.
 *
 * @param request
 * The request to wrap
 * @throws IllegalArgumentException
 * if the request is null
 */
 public SignHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
 super(request);
 body = readBytes(request.getInputStream());
 }
 
 public ServletInputStream getInputStream() throws IOException {
 final ByteArrayInputStream bais = new ByteArrayInputStream(body);
 return new ServletInputStream() {
 
 public int read() throws IOException {
 return bais.read();
 }
 
 public boolean isFinished() {
 return bais.available() == 0;
 }
 
 public boolean isReady() {
 return false;
 }
 
 public void setReadListener(ReadListener arg0) {
 }
 };
 }
 private byte[] readBytes(InputStream in) throws IOException {
 int buffSize = 1024;
 try (BufferedInputStream bis = new BufferedInputStream(in); ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize)) {
 byte[] temp = new byte[buffSize];
 int size;
 while ((size = bis.read(temp)) != -1) {
 out.write(temp, 0, size);
 }
 return out.toByteArray();
 }
 }
 }
- 添加一个 - SignFilter过滤器用来封装请求- 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- package com.sixi.enquiry.interceptor; 
 import java.io.IOException;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.MediaType;
 /**
 * Created with IntelliJ IDEA
 *
 * @author 喵♂呜
 * Created on 2017/12/20 18:58.
 */
 public class SignFilter implements Filter {
 
 public void init(FilterConfig filterConfig) throws ServletException {}
 
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 if (request instanceof HttpServletRequest) {
 HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 // 只处理POST并且ContentType是JSON的请求 这里根据业务情况自行修改
 if (HttpMethod.POST.matches(httpServletRequest.getMethod().toUpperCase()) &&
 httpServletRequest.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
 ServletRequest requestWrapper = new SignHttpServletRequestWrapper((HttpServletRequest) request);
 chain.doFilter(requestWrapper, response);
 return;
 }
 }
 chain.doFilter(request, response);
 }
 
 public void destroy() {}
 }
- 在配置类内新增一个 Bean 注册过滤器 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 public FilterRegistrationBean SignFilterRegistration() {
 FilterRegistrationBean registration = new FilterRegistrationBean();
 registration.setFilter(new SignFilter());
 registration.addUrlPatterns("/*");
 registration.setName("SignFilter");
 registration.setOrder(Integer.MAX_VALUE);
 return registration;
 }
- 新增一个用到了 - getInputStream的拦截器- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24- package com.sixi.enquiry.interceptor; 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StreamUtils;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 /**
 * @author 喵♂呜
 * @since 2017/5/31
 */
 public class SignInterceptor extends HandlerInterceptorAdapter {
 private String SignHeader = "Sixi-Signature";
 
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
 StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
 return true;
 }
 }
- 新增控制器 打印数据 - 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- package com.sixi.enquiry.controller; 
 import java.io.IOException;
 import java.nio.charset.Charset;
 import javax.servlet.http.HttpServletRequest;
 import org.springframework.util.StreamUtils;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 /**
 * Created with IntelliJ IDEA
 *
 * @author 喵♂呜
 * Created on 2017/12/21 19:04.
 */
 public class TestController {
 
 public String test(HttpServletRequest request) throws IOException {
 return StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
 }
 }
- 使用CURL测试 可以获得数据 - 1 
 2
 3
 4- $ curl -sX POST http://172.30.34.3:1221/test \ 
 > -H 'Content-Type: application/json' \
 > -d '{"id":111,"userId":1,"amount":100,"products":[{"id":123,"name":"testproduct","price":23}]}'
 {"id":111,"userId":1,"amount":100,"products":[{"id":123,"name":"testproduct","price":23}]}