为了防止添加拦截器之后只能获取一次流 需要自定义类重写HttpServletRequestWrapper
package com.sinochemitech.flowablehttp.Interceptor; import org.springframework.util.StreamUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; /** * SinochemHttpServletRequestWrapper这个主要是包装HttpServletRequest,因为HttpServletRequest只能读一次流,所以需要一个包装类,否则controller类接收不到参数 * @author jiagang * @summary 自定义 HttpServletRequestWrapper 来包装输入流 */ public class SinochemHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * 缓存下来的HTTP body */ private byte[] body = null; public SinochemHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); try { body = StreamUtils.copyToByteArray(request.getInputStream()); }catch (Exception e){ e.printStackTrace(); } } /** * 重新包装输入流 * @return * @throws IOException */ @Override public ServletInputStream getInputStream() throws IOException { if (body == null) { body = new byte[0]; } final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public void setReadListener(ReadListener listener) { } @Override public boolean isReady() { return false; } @Override public boolean isFinished() { return false; } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } }
拦截器代码如下:
package com.sinochemitech.flowablehttp.Interceptor; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sinochemitech.exception.FlowException; import com.sinochemitech.util.JsonUtil; import com.sinochemitech.util.RSAUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StreamUtils; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; /** * 验签 */ @Component public class InterceptorConfig implements HandlerInterceptor { private static Logger logger = LoggerFactory.getLogger(InterceptorConfig.class); @Value("${flowable.publicKey}") private String publicKey; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { try { // AxinHttpServletRequestWrapper requestWrapper = new AxinHttpServletRequestWrapper(httpServletRequest); // 获取请求路径 String requestURI = httpServletRequest.getRequestURI(); if (!requestURI.contains("api/workFlow")) { return true; } //获取请求参数 String queryString = httpServletRequest.getQueryString(); logger.info("请求参数:{}", queryString); //获取请求body byte[] bodyBytes = StreamUtils.copyToByteArray(httpServletRequest.getInputStream()); String body = new String(bodyBytes, httpServletRequest.getCharacterEncoding()); logger.info("请求体:{}", body); // Map<String, Object> map = JSON.parseObject(body,Map.class); Map<String, Object> map = JsonUtil.jsonToMap(body); String signStr = String.valueOf(map.get("sign")); byte[] sign = org.apache.commons.codec.binary.Base64.decodeBase64(signStr); map.remove("sign"); Set set = map.keySet(); Iterator it = set.iterator(); TreeMap tm = new TreeMap(new RSAUtil.MComparator()); while (it.hasNext()) { String key = String.valueOf(it.next()); tm.put(key, map.get(key)); } // Map<String, Object> keyMap = RSAUtil.initKey(); String mapSortStr = JSONObject.toJSONString(tm); logger.info("传过来的字符串:mapSortStr:{}", mapSortStr); //公钥验证 boolean flagB = RSAUtil.verify(mapSortStr.getBytes(), sign, publicKey); if (flagB) { return true; } else { throw new FlowException("无权限"); } } catch (Exception e) { throw new FlowException(e.getMessage()); } } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { logger.info("兑换服务拦截器-处理请求完成后视图渲染之前的处理操作"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { logger.info("兑换服务拦截器-视图渲染之后的操作"); } }
这个时候有人通过自定义DispatcherServlet的方式来包装成我们自定义的request
// /** // * 包装成我们自定义的request // * @param request // * @param response // * @throws Exception // */ // @Override // protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // super.doDispatch(new AxinHttpServletRequestWrapper(request), response); // } //}
也就是这种方式来创建我们上面自定义的HttpServletRequestWrapper,这种情况可以解决获取一次流的问题,但是涉及到表单接参的接口,就会接受不到值。
以下使用过滤器的方式来创建自定义的HttpServletRequestWrapper
主要通过过滤路径的方式
因为我拦截器里的逻辑是在处理关于签名验证的问题,所以这些路径下的接口需要拦截,并且需要多次获取流的body
package com.sinochemitech.flowablehttp.filter; import com.sinochemitech.flowablehttp.Interceptor.SinochemHttpServletRequestWrapper; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 过滤需要验签的接口,将request转化成自定义包装的request--为了解决request只能获取一次流 * @author : jiagang * @date : Created in 2021/8/26 14:35 */ @WebFilter(filterName = "SinochemFilter", urlPatterns = "/api/workFlow/*") public class SinochemFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; ServletRequest requestWrapper = new SinochemHttpServletRequestWrapper(httpServletRequest); filterChain.doFilter(requestWrapper, servletResponse); } @Override public void destroy() { } }
过滤器配置
@Bean public FilterRegistrationBean<SinochemFilter> Filters() { FilterRegistrationBean<SinochemFilter> registrationBean = new FilterRegistrationBean<SinochemFilter>(); registrationBean.setFilter(new SinochemFilter()); registrationBean.addUrlPatterns("/api/workFlow/*"); registrationBean.setName("koalaSignFilter"); return registrationBean; }