Solve the problem that the input stream of HttpServletRequest can only be read once, and the request can be read repeatedly

Original link: http://www.360doc.com/document/19/0106/20/39911641_807065847.shtml

There is a general description of the business logic of repeatable reading and verification in the original link

Code implementation steps

first step

编写 RequestWrapper类  extends HttpServletRequestWrapper
package com.xihongshi.common.filter;

import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

/**
 * @author mgq
 * @since 2021-03-30 9:59
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
    /**
     * 存储body数据
     */
    private final byte[] body;
    public RequestWrapper(final HttpServletRequest request) {
        super(request);
        // 将body数据存储起来
        String bodyStr=getBodyString(request);
        // 初始化body
        body = bodyStr.getBytes(Charset.defaultCharset());
    }

    public String getBodyString(ServletRequest request) {
        try {
            return inputStream2String(request.getInputStream());
        } catch (IOException e) {
            log.error("",e);
            throw  new RuntimeException(e);
        }
    }

    public String getBodyString() {
        final InputStream byteArrayInputStream = new ByteArrayInputStream(body);
        return inputStream2String(byteArrayInputStream);
    }

    private String inputStream2String(InputStream inputStream) {
        StringBuilder sb = new StringBuilder();
        BufferedReader reder = null;
        try {
            reder=new BufferedReader(new InputStreamReader(inputStream,Charset.defaultCharset()));
            String line;
            while ((line=reder.readLine())!=null){
                sb.append(line);
            }
        } catch (Exception e) {
            log.error("",e);
            throw  new RuntimeException(e);
        }finally {
            if (reder!=null) {
                try {
                    reder.close();
                } catch (IOException e) {
                    log.error("",e);
                }
            }
        }
        return  sb.toString();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(final ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }
}

 Second step

Write the ReplaceStreamFilter class, implements Filter

 

package com.xihongshi.common.filter;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author mgq
 * @since 2021-03-30 10:20
 */
@Slf4j
public class ReplaceStreamFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("StreamFilter初始化");
    }

    @Override
    public void doFilter(final ServletRequest request,  ServletResponse response,  FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
        chain.doFilter(requestWrapper,response);
    }

    @Override
    public void destroy() {
        log.info("StreamFilter销毁");
    }
}

 third step

Write the JWTInterceptor class, implements HandlerInterceptor is used to test to fetch data from the body of the request, and to verify whether the subsequent interface can receive the data in @requestBody

 

package com.xihongshi.client.interceptor;

import com.auth0.jwt.JWT;
import com.xihongshi.common.constants.RedisConstants;
import com.xihongshi.common.errcode.BizErrorCodeEnum;
import com.xihongshi.common.exception.BizException;
import com.xihongshi.common.exception.InvalidSessionException;
import com.xihongshi.common.filter.RepeatableRequestWrapper;
import com.xihongshi.common.filter.RequestWrapper;
import com.xihongshi.utils.JwtUtils;
import com.xihongshi.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.SignUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;

/**
 * 
* @author: mengshaoshuai
* @date 2021年3月12日 下午1:51:20
 */
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {

	@Value("${client.auth.test.enabled:false}")
	private boolean authTestEnabled = false;
	@Value("${client.auth.test.accountId:}")
	private Integer authTestAccountId;

	@Autowired
	JwtUtils jwtUtils;
	
	@Autowired
	RedisUtil redisUtil;
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		String token = request.getHeader("token");
		String sb = new RequestWrapper(request).getBodyString();
		System.out.println(sb);
		try {
			jwtUtils.verify(token);
		}  catch (Exception e) {
			if(authTestEnabled) {

				//test
				request.setAttribute("unionId", authTestAccountId+"");
				request.setAttribute("accountId", authTestAccountId+"");
			}else{
				throw new InvalidSessionException(BizErrorCodeEnum.JWT_TOKEN_ERROR);
			}
			return true;
		}
		String  id = JWT.decode(token).getAudience().get(0);
		String tknow = (String)redisUtil.get(RedisConstants.JWT_KEY_ACCOUNT+id);
		if(tknow==null || !tknow.equals(token)) {
			throw new BizException(BizErrorCodeEnum.JWT_TOKEN_EXPIRE);
		}
		String  mobile = JWT.decode(token).getClaim("mobile").asString();
		request.setAttribute("accountId", id);
		request.setAttribute("unionId", mobile);
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		clearMdc();
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		clearMdc();
	}

	/**
	 * 清理日志上下文
	 */
	protected void clearMdc() {
		try {
			MDC.clear();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

the fourth step

Filter configuration class  
 Note: The fourth and fifth steps does not have to configure, or else steps 1 and 2 do not take effect
package com.xihongshi.client.interceptor;

import com.xihongshi.common.filter.RepeatableReadFilter;
//import com.xihongshi.common.filter.ReplaceStreamFilter;
import com.xihongshi.common.filter.ReplaceStreamFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

/**
 * 过滤配置类
 * @author mgq
 * @since 2021-03-30 10:52
 */
@Configuration
public class FilterConfig {

    /**
     * 注册过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean someFilterRegistration(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(replaceStreamFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("streamFilter");
        return filterRegistrationBean;
    }

    /**
     * 实例化StreamFilter
     * @return
     */
    @Bean(name="replaceStreamFilter")
    public Filter replaceStreamFilter(){
        return new ReplaceStreamFilter();
    }
}

the fifth step

Interceptor configuration
package com.xihongshi.client.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器配置
 * @author mgq
 * @since 2021-03-30 11:12
 */
@Slf4j
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Bean
    public JWTInterceptor getJWTInterceptor(){
        return new JWTInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getJWTInterceptor())
                .addPathPatterns("/**");
    }
}

 Sixth step

Feel free to write a controller interface, the calling parameter in the interface is the json object received by @requestbody

Guess you like

Origin blog.csdn.net/qq_39564710/article/details/115323018