解决HttpServletRequest的输入流只能读取一次的问题,request可重复读

原文连接:http://www.360doc.com/document/19/0106/20/39911641_807065847.shtml

原文连接中有对 可重复读 和 验签业务逻辑的大致说明

代码实现步骤

第一步

编写 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();
            }
        };
    }
}

 第二步

编写 ReplaceStreamFilter类,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销毁");
    }
}

 第三步

编写JWTInterceptor类,implements HandlerInterceptor  用于测试从request的body中取数据,验证后续接口是否能接收到@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();
		}
	}

}

第四步

过滤配置类  
注意:第四不和第五步必须配置,要不然1和2步骤不生效
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();
    }
}

第五步

拦截器配置
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("/**");
    }
}

 第六步

随便写一个controller接口,在接口中调用参数是@requestbody 接收的json对象

猜你喜欢

转载自blog.csdn.net/qq_39564710/article/details/115323018