Spring Boot HandlerInterceptorブロッキング:Required request body is missing OR Stream closed
16388 ワード
requestでgetReader()とgetInputStream()は1回しか呼び出せないため
だからControllerの中の方法の上で@ResponseBodyは再びgetInputStream()を呼び出して2種類の誤りを報告します:
第一:H t t p M e s a g e N otReadableException:Required request body is missing
第二:exception is java.io.IOException: Stream closed
###ブロッキングでは、requestでgetReader()とgetInputStream()を1回だけ呼び出し、inputStreamを繰り返し読み取るrequestを構築します.*requestでgetReader()とgetInputStream()が一度しか呼び出されないため、Controller@ResponseBodyの場合nullまたはStream closed*が取得できません.プロジェクトでは、インタフェースパラメータのチェックなどの問題が発生する可能性があります.
1.RepeatedlyRequestWrapperクラスを追加し、HttpServeretRequestWrapperパッケージクラスを継承する
2、RepeatedlyReadFilterを追加する
3、次にブロック部分にLogHandlerInterceptorクラスを作成します(ここではグローバルに対してToken検証を行います):
4、続いてBoot web要求SpringBootWebConfigをブロックする(WebMvcConfigurerAdapterはSpring 5.0で廃棄された)
最終テスト:
LogHandlerInterceptor
だからControllerの中の方法の上で@ResponseBodyは再びgetInputStream()を呼び出して2種類の誤りを報告します:
第一:H t t p M e s a g e N otReadableException:Required request body is missing
第二:exception is java.io.IOException: Stream closed
###ブロッキングでは、requestでgetReader()とgetInputStream()を1回だけ呼び出し、inputStreamを繰り返し読み取るrequestを構築します.*requestでgetReader()とgetInputStream()が一度しか呼び出されないため、Controller@ResponseBodyの場合nullまたはStream closed*が取得できません.プロジェクトでは、インタフェースパラメータのチェックなどの問題が発生する可能性があります.
1.RepeatedlyRequestWrapperクラスを追加し、HttpServeretRequestWrapperパッケージクラスを継承する
/*
* 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はSpring 5.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);
}