解决HttpRequest 拦截器多次获取inputStream

需求

实现外部接口访问进行签名验证

计划

1.自定义一个注解类
2.定义一个POST请求接口,并用注解标识
3.自定义一个切面和一个过滤器

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface CallbackHandler {

}

post接口

这里需要注意@RequestBody 只能搭配post请求

  @CallbackHandler
  @PostMapping("/dd")
  public Result commonOrderRefund(
      @RequestBody Object request
  ) {
  	//业务处理
    return ResultModel.SUCCESS;
  }

编写切面

错误的写法
对于servketrequest来说,InputStream只能读取一次
InputStream里面有做指针和同步处理,一旦指针到了末尾是不会回来的。那么我们怎么拷贝request body里面的数据呢,当然我们得找一种可以复制的存储方式了,比如String,可以先把request 的inputStream转成String,然后又把String转成byte[] 存回去就是了,String对象我们可以无限使用。

//用一个final request接收,再去读取
final HttpServletRequest httpServletRequest = new ContentCachingRequestWrapper(request);
    String requestBody = ByteSource.wrap(ByteStreams.toByteArray(httpServletRequest.getInputStream())).asCharSource(StandardCharsets.UTF_8).read();
@Slf4j
@RequiredArgsConstructor
public class CallbackInterceptor extends HandlerInterceptorAdapter {
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    if (!(handler instanceof HandlerMethod)) {
      return super.preHandle(request, response, handler);
    }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
    CallbackHandler callbackHandler = Optional
        .ofNullable(handlerMethod.getMethodAnnotation(CallbackHandler.class))
        .orElse(handlerMethod.getBeanType().getAnnotation(CallbackHandler.class));
    if (callbackHandler == null) {
      return super.preHandle(request, response, handler);
    }
	//boolean ok = 签名验证逻辑 如果直接获取 request.get
	//这样就可以轻松取到request的body也不会破坏request
	String requestBody = ((RequestWrapper) request).getBody()
    if (ok) {
      log.info("恭喜签名成功,快过去干活吧");
      return super.preHandle(request, response, handler);
    } else {
      this.response(response);
      return false;
    }
    
    }
}

自定义一个Wrapper

@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

  private final String body;

  public RequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    StringBuilder stringBuilder = new StringBuilder("");
    BufferedReader bufferedReader = null;
    try {
      InputStream inputStream = request.getInputStream();
      if (inputStream != null) {
        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        char[] charBuffer = new char[128];
        int bytesRead = -1;
        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
          stringBuilder.append(charBuffer, 0, bytesRead);
        }
      }
    } catch (IOException ex) {
      throw ex;
    } finally {
      if (bufferedReader != null) {
        try {
          bufferedReader.close();
        } catch (IOException ex) {
          log.error("关闭流失败",ex);
        }
      }
    }
    body = stringBuilder.toString();
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
    ServletInputStream servletInputStream = new ServletInputStream() {
      public boolean isFinished() {
        return false;
      }

      public boolean isReady() {
        return false;
      }

      public void setReadListener(ReadListener readListener) {
      }

      public int read() throws IOException {
        return byteArrayInputStream.read();
      }
    };
    return servletInputStream;

  }

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

  public String getBody() {
    return this.body;
  }

}

定义一个过滤器

public class HttpRequestWrapperFilter extends OncePerRequestFilter {

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    boolean isMultipart = StrUtil.startWith(request.getContentType(), "multipart");
    filterChain.doFilter(isMultipart
            ? request
            : new RequestWrapper(request),
        response);
  }
}

写一个配置类

@Configuration
@EnableWebMvc
@RequiredArgsConstructor
public class SignInterceptorConfiguration implements WebMvcConfigurer {

  private final SupplierMapper supplierMapper;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    CallbackInterceptor signInterceptor = new CallbackInterceptor(supplierMapper);
    registry.addInterceptor(signInterceptor).addPathPatterns("/**/dd/**");
  }

  @Bean
  public FilterRegistrationBean httpRequestWrapperFilter() {
    HttpRequestWrapperFilter httpRequestWrapperFilter = new HttpRequestWrapperFilter();
    FilterRegistrationBean<HttpRequestWrapperFilter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
    filterFilterRegistrationBean.setFilter(new HttpRequestWrapperFilter());
    filterFilterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
    filterFilterRegistrationBean.setName("request_wrapper");
    return filterFilterRegistrationBean;
  }

}

以上就是我的代码总结,欢迎交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值