Bootstrap

实现基于Filter拦截并修改请求参数(参数解密等场景)

背景
  • 对服务接口的安全性具有一定要求,需要做签名验证;
  • 对服务接口的请求参数做解密还原处理;
实现逻辑
  • 自定义过滤器实现Filter,并注册到SpringBoot
  • 通过将ServletRequest转换为自定义包装器实现自由读取InputStream
  • 根据具体的请求方法类型和业务参数加密需求进行参数的解密、重组等
  • 将新的参数请求体存入包装器内产生新的InputStream
  • 将包装器和响应对象传入FilterChain进行后续业务
过滤器实现代码
/**
 * <p>
 * 参数过滤器
 * </p>
 *
 * @author zhengshangjin
 * @version 1.0.0
 * @since 1.0.0
 * created on 2020-04-03
 */
@Slf4j
@Component
public class ServerParamFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      
      	// 将ServletRequest对象转换为自定义包装器对象(见:《实现ServletRequest流重复读、流重写场景》文章)
        CoverHttpServletRequest requestWrapper = new CoverHttpServletRequest((HttpServletRequest) request);

        // 根据不同的请求方法类型 做 不同的参数处理业务 并返回新的包装器
        if (HttpMethod.POST.matches(requestWrapper.getMethod())) {
            requestWrapper = handlePostCommonParam(requestWrapper);
        }
        if (HttpMethod.GET.matches(requestWrapper.getMethod())) {
            requestWrapper = handleGetCommonParam(requestWrapper);
        }
      
      	// 继续执行
        chain.doFilter(requestWrapper, response);
    }

    /**
     * 处理GET公共参数
     *
     * @param requestWrapper 请求包装器
     * @return CoverHttpServletRequest
     * @author zhengshangjin
     * created on 2020-04-03
     */
    public CoverHttpServletRequest handleGetCommonParam(CoverHttpServletRequest requestWrapper) {
      
      	// 业务逻辑
        // 整理后的公共参数可以存放在 RequestAttributes 或者 ThreadLocal 中 以便后续调用
      
        return requestWrapper;
    }

    /**
     * 处理POST公共参数
     *
     * @param requestWrapper 请求包装器
     * @return CoverHttpServletRequest
     * @author zhengshangjin
     * created on 2020-04-03
     */
    public CoverHttpServletRequest handlePostCommonParam(CoverHttpServletRequest requestWrapper) {
        // 这边就可以直接获取body了
        String body = requestWrapper.getBody();
        if (StringUtils.isEmpty(body) || !JsonUtils.isValidJson(body)) {
            return requestWrapper;
        }
      
        // 对body进行其他的业务逻辑处理,如参数重组,参数解密等等。
        // 整理后的公共参数可以存放在 RequestAttributes 或者 ThreadLocal 中 以便后续调用
        // 具体的业务参数应当还原为body中,对应Controller中可直接解析的参数对象
      
        // 将更改后的body参数装入包装器内
        requestWrapper.setBody(data);
        return requestWrapper;
    }


}

过滤器注册实现

关于过滤器注册的实现方式在SpringBoot中有多种,这里列举两种注册实现方式。

基于注解实现

通过 @WebFilter 注解来标记一个过滤器

/**
 * <p>
 * 参数过滤器
 * </p>
 *
 * @author zhengshangjin
 * @version 1.0.0
 * @since 1.0.0
 * created on 2020-04-03
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "serverParamFilter")
public class ServerParamFilter implements Filter {
  
}
  • 该注解有多个参数,具体可见源码,这里给定参数urlPatterns表示拦截所有根下的请求路径,指定filterName为过滤器的名称。
  • 这种方式有一个很大的弊端就是无法指定Filter的优先级,如果存在多个 Filter 时,无法通过 @Order 指定优先级。
自行注册Bean实现
/**
 * <p>
 * 过滤器配置
 * </p>
 *
 * @author zhengshangjin
 * @version 1.0.0
 * @since 1.0.0
 * created on 2020-04-03
 */
@Configuration
public class FilterConfiguration {
  
    /**
     * 创建一个名为serverParamFilter的bean
     */
    @Bean
    public ServerParamFilter serverParamFilter() {
        return new ServerParamFilter();
    }

    /**
     * 注册过滤器
     */
    @Bean
    public FilterRegistrationBean<?> serverParamFilterRegister() {
        FilterRegistrationBean<?> registration = new FilterRegistrationBean<>(serverParamFilter());
        // 设置拦截资源路径
        registration.addUrlPatterns(Arrays.asList("/*"));
        // 如果存在多个过滤器,可通过此参数指定过滤器执行的先后顺序
        registration.setOrder(1);
        return registration;
    } 
  
}
  • 通过此种方式注册Filter虽然相较于上面一种稍显复杂,但是不仅可以自定义拦截资源规则,还可以实现多个Filter下自定义执行的先后顺序。
;