SpringBoot2.X自定义参数拦截器,同一请求被拦截两次处理方法, redis在拦截器中无法加载的问题
场景:
java和php两个平台的登录模块不同,现在php需要调用java的一个接口,目前想到是通过redis中的token校验,有好的方法请大神指点。
想自定义一个拦截器,因为需要redis中的token数据,所有需要在拦截器中注入redis工具类,就用了构造方法加载;
之后发现每次请求都会被拦截两次,然后再拦截器中打印request.getRequestURI() 发现第一次是请求地址,第二次是/error地址,所以在代码中添加excludePathPatterns("/error");
总感觉此方法不是很好,如有不对请指正。
1、自定义拦截器
import cn.fookey.payment.utils.RedisOperator; import com.alibaba.fastjson.JSONObject; import java.io.PrintWriter; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; /** * * http请求拦截器 * @Author TCL * @Date 2021/4/26 16:53 * @Version 1.0 */ @Component public class RequestInterceptor implements HandlerInterceptor { private RedisOperator redisOperator; //拦截器优先bean的加载,所以使用构造方法 public RequestInterceptor (RedisOperator redisOperator) { this.redisOperator = redisOperator; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception { //获取请求body //RequestParamsWrapper类 解决request请求流只能读取一次的问题 String bodyString = new RequestParamsWrapper(request).getBodyString(); Map<String,Object> map = JSONObject.parseObject(bodyString, Map.class); // 获取redis中token String redisToken = redisOperator.get(map.get("accountId")); // 校验token if (!map.get("token").equals(redisToken)) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); writer.print("{\"status\": 500,\"msg\": \"用户令牌失效!\",\"data\": null}"); return false; } return true; } }
2、拦截器中,request中参数只能调用一次,因为是自定义参数,在拦截器中使用参数以后到controller中就获取不到了,所有添加一个请求参数包装器。
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import lombok.extern.slf4j.Slf4j; /** * 请求参数包装器 * springboot拦截器校验或鉴权导致Required request body is missing解决方法 * 解决思路:重新定义一个HttpServletRequestWrapper来代替request,里面的流复制保存,就不会丢失 * @Author TCL * @Date 2021/4/26 9:57 * @Version 1.0 */ @Slf4j public class RequestParamsWrapper extends HttpServletRequestWrapper { /** * 存储body数据的容器 */ private final byte[] body; public RequestParamsWrapper(HttpServletRequest request) throws IOException { super(request); // 将body数据存储起来 String bodyStr = getBodyString(request); body = bodyStr.getBytes(Charset.defaultCharset()); } /** * 获取请求Body * * @param request request * @return String */ public String getBodyString(final ServletRequest request) { try { return inputStream2String(request.getInputStream()); } catch (IOException e) { log.error("", e); throw new RuntimeException(e); } } /** * 获取请求Body * * @return String */ public String getBodyString() { final InputStream inputStream = new ByteArrayInputStream(body); return inputStream2String(inputStream); } /** * 将inputStream里的数据读取出来并转换成字符串 * * @param inputStream inputStream * @return String */ private String inputStream2String(InputStream inputStream) { StringBuilder sb = new StringBuilder(); BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset())); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { log.error("", e); throw new RuntimeException(e); } finally { if (reader != null) { try { reader.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 int read() throws IOException { return inputStream.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } }
3、配置WebMvcConfigurer
import cn.fookey.payment.utils.RedisOperator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * * @Author TCL * @Date 2021/4/26 17:11 * @Version 1.0 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private RedisOperator redisOperator; //过滤器 @Bean public FilterRegistrationBean<RequestParamsFilter> Filters() { FilterRegistrationBean<RequestParamsFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new RequestParamsFilter()); registrationBean.addUrlPatterns("/*"); return registrationBean; } //自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RequestInterceptor(redisOperator)) .excludePathPatterns("classpath:/META-INF/resources/") //同一请求被拦截两次处理方法 .excludePathPatterns("/error") .addPathPatterns("/**"); } }
4、重写过滤器中的方法
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; /** * * @Author TCL * @Date 2021/4/26 15:30 * @Version 1.0 */ @WebFilter(filterName="bodyReaderFilter",urlPatterns="/*") public class RequestParamsFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // do nothing } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper=null; if(request instanceof HttpServletRequest) { requestWrapper=new RequestParamsWrapper((HttpServletRequest)request); } if(requestWrapper==null) { chain.doFilter(request, response); }else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { // do nothing } }
以上哪里有不对,请指正,谢谢。