背景
- 对服务接口的安全性具有一定要求,需要做签名验证;
- 对服务接口的请求参数做解密还原处理;
实现逻辑
- 自定义过滤器实现
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
下自定义执行的先后顺序。