Spring Boot HandlerInterceptor拦截器 :Required request body is missing OR Stream closed

- 日理万妓 2022-03-08 02:20 1013阅读 0赞

由于 request中getReader()和getInputStream()只能调用一次

所以在Controller里面方法上@ResponseBody回再次调用一次getInputStream()报错2种错误:

第一:HttpMessageNotReadableException: Required request body is missing

第二:exception is java.io.IOException: Stream closed

### 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.
* 由于 request中getReader()和getInputStream()只能调用一次 导致在Controller @ResponseBody的时候获取不到 null 或Stream closed
* 在项目中,可能会出现需要针对接口参数进行校验等问题 如:Token

1、添加RepeatedlyRequestWrapper 类并继承 HttpServletRequestWrapper 包装类

  1. /*
  2. * Copyright (c) 2019-2019 1-meifen.com
  3. * 1-meifen.com PROPRIETARY/CONFIDENTIAL.
  4. * All rights reserved.
  5. * author qierkang xyqierkang@163.com
  6. *
  7. */
  8. package com.ymeifen.filter;
  9. import com.ymeifen.StringUtils;
  10. import javax.servlet.ReadListener;
  11. import javax.servlet.ServletInputStream;
  12. import javax.servlet.http.HttpServletRequest;
  13. import javax.servlet.http.HttpServletRequestWrapper;
  14. import java.io.BufferedReader;
  15. import java.io.ByteArrayInputStream;
  16. import java.io.IOException;
  17. import java.io.InputStreamReader;
  18. import java.nio.charset.Charset;
  19. /**
  20. * @Title RepeatedlyReadRequestWrapper
  21. * @ProjectName com.ymeifen.filter
  22. * @Author qierkang xyqierkang@163.com
  23. * @Date Created in 2019-03-14 00:20
  24. * @Description [ 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.
  25. * 由于 request中getReader()和getInputStream()只能调用一次 导致在Controller @ResponseBody的时候获取不到 null 或Stream closed
  26. * 在项目中,可能会出现需要针对接口参数进行校验等问题 如:Token
  27. *
  28. * ]
  29. */
  30. public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
  31. private final byte[] body;
  32. public RepeatedlyRequestWrapper(HttpServletRequest request)
  33. throws IOException {
  34. super(request);
  35. body = readBytes(request.getReader(), "utf-8");
  36. }
  37. @Override
  38. public BufferedReader getReader() throws IOException {
  39. return new BufferedReader(new InputStreamReader(getInputStream()));
  40. }
  41. @Override
  42. public ServletInputStream getInputStream() throws IOException {
  43. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  44. return new ServletInputStream() {
  45. @Override
  46. public boolean isFinished() {
  47. return false;
  48. }
  49. @Override
  50. public boolean isReady() {
  51. return false;
  52. }
  53. @Override
  54. public void setReadListener(ReadListener listener) {
  55. }
  56. @Override
  57. public int read() throws IOException {
  58. return bais.read();
  59. }
  60. };
  61. }
  62. /**
  63. * 通过BufferedReader和字符编码集转换成byte数组
  64. * @param br
  65. * @param encoding
  66. * @return
  67. * @throws IOException
  68. */
  69. private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
  70. String str = null,retStr="";
  71. while ((str = br.readLine()) != null) {
  72. retStr += str;
  73. }
  74. if (StringUtils.isNotBlank(retStr)) {
  75. return retStr.getBytes(Charset.forName(encoding));
  76. }
  77. return null;
  78. }
  79. }

2、添加RepeatedlyReadFilter 过滤器

  1. /*
  2. * Copyright (c) 2019-2019 1-meifen.com
  3. * 1-meifen.com PROPRIETARY/CONFIDENTIAL.
  4. * All rights reserved.
  5. * author qierkang xyqierkang@163.com
  6. *
  7. */
  8. package com.ymeifen.filter;
  9. import org.slf4j.Logger;
  10. import org.slf4j.LoggerFactory;
  11. import javax.servlet.*;
  12. import javax.servlet.http.HttpServletRequest;
  13. import java.io.IOException;
  14. /**
  15. * @Title RepeatedlyReadFilter
  16. * @ProjectName com.ymeifen.filter
  17. * @Author qierkang xyqierkang@163.com
  18. * @Date Created in 2019-03-14 00:21
  19. * @Description [ 一句话描述是什么作用 ]
  20. */
  21. public class RepeatedlyReadFilter implements Filter {
  22. private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
  23. @Override
  24. public void init(FilterConfig filterConfig) throws ServletException {
  25. }
  26. @Override
  27. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  28. // logger.debug("复制request.getInputStream流");
  29. ServletRequest requestWrapper = null;
  30. if (request instanceof HttpServletRequest) {
  31. requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request);
  32. }
  33. if (null == requestWrapper) {
  34. chain.doFilter(request, response);
  35. } else {
  36. chain.doFilter(requestWrapper, response);
  37. }
  38. }
  39. @Override
  40. public void destroy() {
  41. }
  42. }

3、接着是拦截器部分 创建**LogHandlerInterceptor类**,(这边针对了全局进行Token验证):

  1. package com.ymeifen.filter;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.google.common.reflect.TypeToken;
  4. import com.google.gson.Gson;
  5. import com.google.gson.JsonObject;
  6. import com.ymeifen.DateUtils;
  7. import com.ymeifen.properties.ManageConfig;
  8. import com.ymeifen.response.BaseResponse;
  9. import com.ymeifen.service.RedisService;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Component;
  14. import org.springframework.web.servlet.ModelAndView;
  15. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  16. import springfox.documentation.spring.web.json.Json;
  17. import javax.annotation.PostConstruct;
  18. import javax.servlet.ServletInputStream;
  19. import javax.servlet.ServletRequest;
  20. import javax.servlet.http.HttpServletRequest;
  21. import javax.servlet.http.HttpServletResponse;
  22. import java.io.*;
  23. import java.nio.charset.Charset;
  24. import java.util.Arrays;
  25. import java.util.List;
  26. /**
  27. * @author qierkang xyqierkang@163.com
  28. * @Title: LogHandlerInterceptor.java
  29. * @date 2018年6月12日 上午3:31:46
  30. * @Description: TODO[拦截器 ]
  31. */
  32. @Component
  33. public class LogHandlerInterceptor extends HandlerInterceptorAdapter {
  34. private static Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class);
  35. /**
  36. * @Fields urls : TODO[ 设置白名单用户 ]
  37. */
  38. private static String[] url = {"/manage/user/login","/manage/user/loginout", "/error"};
  39. public List<String> urlList = Arrays.asList(url);
  40. @Autowired
  41. private RedisService redisService;
  42. @Autowired
  43. private ManageConfig manageConfig;
  44. @PostConstruct
  45. private void init() {
  46. try {
  47. logger.info("EK初始化运营系统拦截器:[{}]操作时间[{}]",manageConfig.getPermOpen()==0?"❌拦截器关闭❌":"?拦截器开启?", DateUtils.getDateTime());
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. /**
  53. * @param @param req
  54. * @param @param response
  55. * @param @return
  56. * @param @throws Exception 设定文件
  57. * @throws
  58. * @author qierkang xyqierkang@163.com
  59. * @date 2018年1月4日 下午7:44:52
  60. * @Description: TODO[ 无权限访问返回 ]
  61. */
  62. private boolean responseNoPerm(HttpServletRequest req, HttpServletResponse response) throws Exception {
  63. PrintWriter out = null;
  64. response.setContentType("application/json;charset=UTF-8");
  65. out = response.getWriter();
  66. out.print(JSONObject.toJSONString(BaseResponse.errorNoPerm()));
  67. out.flush();
  68. return false;
  69. }
  70. private boolean responseTokenIsNull(HttpServletRequest req, HttpServletResponse response) throws Exception {
  71. PrintWriter out = null;
  72. response.setContentType("application/json;charset=UTF-8");
  73. out = response.getWriter();
  74. out.print(JSONObject.toJSONString(BaseResponse.errorNoToken()));
  75. out.flush();
  76. return false;
  77. }
  78. /**
  79. * *
  80. * controller 执行之前调用
  81. */
  82. @Override
  83. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  84. throws Exception {
  85. RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
  86. Gson gson = new Gson();
  87. if (manageConfig.getPermOpen() == 0) {
  88. return true;
  89. } else if (manageConfig.getPermOpen() == 1) {
  90. String url = request.getRequestURI().substring(request.getRequestURI().indexOf("/")+1);
  91. if (urlList.contains(url)) {
  92. //判断白名单是否存在合法url
  93. return true;
  94. }
  95. List<String> list = gson.fromJson(redisService.get("permUrlList"), new TypeToken<List<String>>() {}.getType());
  96. if (list == null || list.size() <= 0) {
  97. //非法连接 没有任何权限
  98. return this.responseNoPerm(request, response);
  99. }
  100. if(request.getParameter("token")==null){
  101. //post json提交判断方法
  102. JSONObject json= JSONObject.parseObject(getBodyString(requestWrapper));
  103. System.out.println(json);
  104. if(null==redisService.get(json.getString("token"))){
  105. //在判断白名单之后 在进行每次进行token判断是否失效
  106. return this.responseTokenIsNull(request, response);
  107. }
  108. }else{
  109. // get / post提交判断方法
  110. if(null==redisService.get(request.getParameter("token"))){
  111. //在判断白名单之后 在进行每次进行token判断是否失效
  112. return this.responseTokenIsNull(request, response);
  113. }
  114. }
  115. if (list.contains(url)) {
  116. return true;
  117. } else {
  118. return this.responseNoPerm(request, response);
  119. }
  120. }
  121. return this.responseNoPerm(request, response);
  122. }
  123. /**
  124. * controller 执行之后,且页面渲染之前调用
  125. */
  126. @Override
  127. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
  128. ModelAndView modelAndView) throws Exception {
  129. // System.out.println("------postHandle执行之后,且页面渲染之前调用-----");
  130. }
  131. /**
  132. * 页面渲染之后调用,一般用于资源清理操作
  133. */
  134. @Override
  135. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
  136. throws Exception {
  137. // System.out.println("------afterCompletion 页面渲染之后调用,一般用于资源清理操作-----");
  138. }
  139. /**
  140. * 获取请求Body
  141. *
  142. * @param request
  143. *
  144. * @return
  145. */
  146. public static String getBodyString(final ServletRequest request) {
  147. StringBuilder sb = new StringBuilder();
  148. InputStream inputStream = null;
  149. BufferedReader reader = null;
  150. try {
  151. inputStream = cloneInputStream(request.getInputStream());
  152. reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
  153. String line = "";
  154. while ((line = reader.readLine()) != null) {
  155. sb.append(line);
  156. }
  157. } catch (IOException e) {
  158. e.printStackTrace();
  159. } finally {
  160. if (inputStream != null) {
  161. try {
  162. inputStream.close();
  163. } catch (IOException e) {
  164. e.printStackTrace();
  165. }
  166. }
  167. if (reader != null) {
  168. try {
  169. reader.close();
  170. } catch (IOException e) {
  171. e.printStackTrace();
  172. }
  173. }
  174. }
  175. return sb.toString();
  176. }
  177. /**
  178. * Description: 复制输入流</br>
  179. *
  180. * @param inputStream
  181. *
  182. * @return</br>
  183. */
  184. public static InputStream cloneInputStream(ServletInputStream inputStream) {
  185. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  186. byte[] buffer = new byte[1024];
  187. int len;
  188. try {
  189. while ((len = inputStream.read(buffer)) > -1) {
  190. byteArrayOutputStream.write(buffer, 0, len);
  191. }
  192. byteArrayOutputStream.flush();
  193. } catch (IOException e) {
  194. e.printStackTrace();
  195. }
  196. InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
  197. return byteArrayInputStream;
  198. }
  199. }

4、接着Boot web 请求 拦截SpringBootWebConfig (WebMvcConfigurerAdapter 在Spring5.0已被废弃

  1. package com.ymeifen.filter;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.http.converter.HttpMessageConverter;
  6. import org.springframework.http.converter.StringHttpMessageConverter;
  7. import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
  8. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  9. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  10. import java.nio.charset.Charset;
  11. import java.util.List;
  12. /**
  13. * @Title: SpringBootWebConfig.java
  14. * @author qierkang xyqierkang@163.com
  15. * @date 2019年03月14日01:14:47
  16. * @Description: TODO[ 初始化拦截器 ]
  17. */
  18. @Configuration
  19. public class SpringBootWebConfig extends WebMvcConfigurerAdapter {
  20. @Autowired
  21. private LogHandlerInterceptor logHandlerInterceptor;
  22. /* (非 Javadoc)
  23. * <p>Title: addInterceptors</p>
  24. * <p>Description: </p>
  25. * @param registry
  26. * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry)
  27. *初始化拦截器
  28. */
  29. @Override
  30. public void addInterceptors(InterceptorRegistry registry) {
  31. registry.addInterceptor(logHandlerInterceptor).addPathPatterns("/**");;
  32. }
  33. @Bean
  34. public HttpMessageConverter<String> responseBodyConverter() {
  35. StringHttpMessageConverter converter = new StringHttpMessageConverter(
  36. Charset.forName("UTF-8"));
  37. return converter;
  38. }
  39. @Override
  40. public void configureMessageConverters(
  41. List<HttpMessageConverter<?>> converters) {
  42. super.configureMessageConverters(converters);
  43. }
  44. @Override
  45. public void configureContentNegotiation(
  46. ContentNegotiationConfigurer configurer) {
  47. configurer.favorPathExtension(false);
  48. }
  49. }

最后测试:

LogHandlerInterceptor

  1. //在这里使用
  2. //RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
  3. //获取多次也不会影响到 因为InputStream 流被复制 Controller @ResponseBody 也不会获取不到
  4. @Override
  5. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  6. throws Exception {
  7. RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
  8. Gson gson = new Gson();
  9. if (manageConfig.getPermOpen() == 0) {
  10. return true;
  11. } else if (manageConfig.getPermOpen() == 1) {
  12. String url = request.getRequestURI().substring(request.getRequestURI().indexOf("/")+1);
  13. if (urlList.contains(url)) {
  14. //判断白名单是否存在合法url
  15. return true;
  16. }
  17. List<String> list = gson.fromJson(redisService.get("permUrlList"), new TypeToken<List<String>>() {}.getType());
  18. if (list == null || list.size() <= 0) {
  19. //非法连接 没有任何权限
  20. return this.responseNoPerm(request, response);
  21. }
  22. if(request.getParameter("token")==null){
  23. //post json提交判断方法
  24. JSONObject json= JSONObject.parseObject(getBodyString(requestWrapper));
  25. System.out.println(json);
  26. if(null==redisService.get(json.getString("token"))){
  27. //在判断白名单之后 在进行每次进行token判断是否失效
  28. return this.responseTokenIsNull(request, response);
  29. }
  30. }else{
  31. // get / post提交判断方法
  32. if(null==redisService.get(request.getParameter("token"))){
  33. //在判断白名单之后 在进行每次进行token判断是否失效
  34. return this.responseTokenIsNull(request, response);
  35. }
  36. }
  37. if (list.contains(url)) {
  38. return true;
  39. } else {
  40. return this.responseNoPerm(request, response);
  41. }
  42. }
  43. return this.responseNoPerm(request, response);
  44. }

发表评论

表情:
评论列表 (有 0 条评论,1013人围观)

还没有评论,来说两句吧...

相关阅读