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パッケージクラスを継承する
/*
 * 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);
    }