由于 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 包装类
/*
* Copyright (c) 2019-2019 1-meifen.com
* 1-meifen.com PROPRIETARY/CONFIDENTIAL.
* All rights reserved.
* author qierkang [email protected]
*
*/
package com.ymeifen.filter;
import com.ymeifen.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
/**
* @Title RepeatedlyReadRequestWrapper
* @ProjectName com.ymeifen.filter
* @Author qierkang [email protected]
* @Date Created in 2019-03-14 00:20
* @Description [ 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.
* 由于 request中getReader()和getInputStream()只能调用一次 导致在Controller @ResponseBody的时候获取不到 null 或Stream closed
* 在项目中,可能会出现需要针对接口参数进行校验等问题 如:Token
*
* ]
*/
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public RepeatedlyRequestWrapper(HttpServletRequest request)
throws IOException {
super(request);
body = readBytes(request.getReader(), "utf-8");
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
}
/**
* 通过BufferedReader和字符编码集转换成byte数组
* @param br
* @param encoding
* @return
* @throws IOException
*/
private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
String str = null,retStr="";
while ((str = br.readLine()) != null) {
retStr += str;
}
if (StringUtils.isNotBlank(retStr)) {
return retStr.getBytes(Charset.forName(encoding));
}
return null;
}
}
2、添加RepeatedlyReadFilter 过滤器
/*
* Copyright (c) 2019-2019 1-meifen.com
* 1-meifen.com PROPRIETARY/CONFIDENTIAL.
* All rights reserved.
* author qierkang [email protected]
*
*/
package com.ymeifen.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Title RepeatedlyReadFilter
* @ProjectName com.ymeifen.filter
* @Author qierkang [email protected]
* @Date Created in 2019-03-14 00:21
* @Description [ 一句话描述是什么作用 ]
*/
public class RepeatedlyReadFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// logger.debug("复制request.getInputStream流");
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request);
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}
3、接着是拦截器部分 创建LogHandlerInterceptor类,(这边针对了全局进行Token验证):
package com.ymeifen.filter;
import com.alibaba.fastjson.JSONObject;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ymeifen.DateUtils;
import com.ymeifen.properties.ManageConfig;
import com.ymeifen.response.BaseResponse;
import com.ymeifen.service.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import springfox.documentation.spring.web.json.Json;
import javax.annotation.PostConstruct;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
/**
* @author qierkang [email protected]
* @Title: LogHandlerInterceptor.java
* @date 2018年6月12日 上午3:31:46
* @Description: TODO[拦截器 ]
*/
@Component
public class LogHandlerInterceptor extends HandlerInterceptorAdapter {
private static Logger logger = LoggerFactory.getLogger(LogHandlerInterceptor.class);
/**
* @Fields urls : TODO[ 设置白名单用户 ]
*/
private static String[] url = {"/manage/user/login","/manage/user/loginout", "/error"};
public List urlList = Arrays.asList(url);
@Autowired
private RedisService redisService;
@Autowired
private ManageConfig manageConfig;
@PostConstruct
private void init() {
try {
logger.info("EK初始化运营系统拦截器:[{}]操作时间[{}]",manageConfig.getPermOpen()==0?"❌拦截器关闭❌":"拦截器开启", DateUtils.getDateTime());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param @param req
* @param @param response
* @param @return
* @param @throws Exception 设定文件
* @throws
* @author qierkang [email protected]
* @date 2018年1月4日 下午7:44:52
* @Description: TODO[ 无权限访问返回 ]
*/
private boolean responseNoPerm(HttpServletRequest req, HttpServletResponse response) throws Exception {
PrintWriter out = null;
response.setContentType("application/json;charset=UTF-8");
out = response.getWriter();
out.print(JSONObject.toJSONString(BaseResponse.errorNoPerm()));
out.flush();
return false;
}
private boolean responseTokenIsNull(HttpServletRequest req, HttpServletResponse response) throws Exception {
PrintWriter out = null;
response.setContentType("application/json;charset=UTF-8");
out = response.getWriter();
out.print(JSONObject.toJSONString(BaseResponse.errorNoToken()));
out.flush();
return false;
}
/**
* *
* controller 执行之前调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
Gson gson = new Gson();
if (manageConfig.getPermOpen() == 0) {
return true;
} else if (manageConfig.getPermOpen() == 1) {
String url = request.getRequestURI().substring(request.getRequestURI().indexOf("/")+1);
if (urlList.contains(url)) {
//判断白名单是否存在合法url
return true;
}
List list = gson.fromJson(redisService.get("permUrlList"), new TypeToken>() {}.getType());
if (list == null || list.size() <= 0) {
//非法连接 没有任何权限
return this.responseNoPerm(request, response);
}
if(request.getParameter("token")==null){
//post json提交判断方法
JSONObject json= JSONObject.parseObject(getBodyString(requestWrapper));
System.out.println(json);
if(null==redisService.get(json.getString("token"))){
//在判断白名单之后 在进行每次进行token判断是否失效
return this.responseTokenIsNull(request, response);
}
}else{
// get / post提交判断方法
if(null==redisService.get(request.getParameter("token"))){
//在判断白名单之后 在进行每次进行token判断是否失效
return this.responseTokenIsNull(request, response);
}
}
if (list.contains(url)) {
return true;
} else {
return this.responseNoPerm(request, response);
}
}
return this.responseNoPerm(request, response);
}
/**
* controller 执行之后,且页面渲染之前调用
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// System.out.println("------postHandle执行之后,且页面渲染之前调用-----");
}
/**
* 页面渲染之后调用,一般用于资源清理操作
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// System.out.println("------afterCompletion 页面渲染之后调用,一般用于资源清理操作-----");
}
/**
* 获取请求Body
*
* @param request
*
* @return
*/
public static String getBodyString(final ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = cloneInputStream(request.getInputStream());
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
/**
* Description: 复制输入流
*
* @param inputStream
*
* @return
*/
public static InputStream cloneInputStream(ServletInputStream inputStream) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
return byteArrayInputStream;
}
}
4、接着Boot web 请求 拦截SpringBootWebConfig (WebMvcConfigurerAdapter 在Spring5.0已被废弃)
package com.ymeifen.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.nio.charset.Charset;
import java.util.List;
/**
* @Title: SpringBootWebConfig.java
* @author qierkang [email protected]
* @date 2019年03月14日01:14:47
* @Description: TODO[ 初始化拦截器 ]
*/
@Configuration
public class SpringBootWebConfig extends WebMvcConfigurerAdapter {
@Autowired
private LogHandlerInterceptor logHandlerInterceptor;
/* (非 Javadoc)
* Title: addInterceptors
* Description:
* @param registry
* @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry)
*初始化拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(logHandlerInterceptor).addPathPatterns("/**");;
}
@Bean
public HttpMessageConverter responseBodyConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
return converter;
}
@Override
public void configureMessageConverters(
List> converters) {
super.configureMessageConverters(converters);
}
@Override
public void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}
最后测试:
LogHandlerInterceptor
//在这里使用
//RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
//获取多次也不会影响到 因为InputStream 流被复制 Controller @ResponseBody 也不会获取不到
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
RepeatedlyRequestWrapper requestWrapper = (RepeatedlyRequestWrapper) request;
Gson gson = new Gson();
if (manageConfig.getPermOpen() == 0) {
return true;
} else if (manageConfig.getPermOpen() == 1) {
String url = request.getRequestURI().substring(request.getRequestURI().indexOf("/")+1);
if (urlList.contains(url)) {
//判断白名单是否存在合法url
return true;
}
List list = gson.fromJson(redisService.get("permUrlList"), new TypeToken>() {}.getType());
if (list == null || list.size() <= 0) {
//非法连接 没有任何权限
return this.responseNoPerm(request, response);
}
if(request.getParameter("token")==null){
//post json提交判断方法
JSONObject json= JSONObject.parseObject(getBodyString(requestWrapper));
System.out.println(json);
if(null==redisService.get(json.getString("token"))){
//在判断白名单之后 在进行每次进行token判断是否失效
return this.responseTokenIsNull(request, response);
}
}else{
// get / post提交判断方法
if(null==redisService.get(request.getParameter("token"))){
//在判断白名单之后 在进行每次进行token判断是否失效
return this.responseTokenIsNull(request, response);
}
}
if (list.contains(url)) {
return true;
} else {
return this.responseNoPerm(request, response);
}
}
return this.responseNoPerm(request, response);
}