⭐ Filter 过滤器实现:
JavaWeb
组件 Servlet
提供了 Filter
过滤功能,其功能是对目标资源的请求和响应进行拦截,对拦截到的请求和响应做出特殊的功能处理,比如我们请求中有一些敏感信息过滤就是利用过滤器过滤。
1.Filter 原理:
Java Servlet API
中提供了Filter
接口,编写Filter的实现类,从而实现自定义过滤器。Filter
的请求流程为:
- 客户端发起请求
- 服务容器判断当前请求资源是否有过滤器,有则执行过滤器
- 过滤器过滤通过后请求到Servlet服务器
- 返回结果通过过滤器返回给请求方
package javax.servlet; import java.io.IOException; public interface Filter { default void init(FilterConfig filterConfig) throws ServletException { } void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; default void destroy() { } }
init()
:此方法在只在过滤器创建的时候执行一次,用于初始化过滤器的属性doFilter()
:该方法会对请求进行拦截,用户需要在该方法中自定义对请求内容以及响应内容进行过滤的,调用该方法的入参FilterChain
对象的doFilter
方法对请求放行执行后面的逻辑,若未调用doFilter
方法则本次请求结束,并向客户端返回响应失败destroy()
:此方法用于销毁过滤器,过滤器被创建以后只要项目一直运行,过滤器就会一直存在,在项目停止时,会调用该方法销毁过滤器
2.Spring Boot 实现 Filter:
在SpringBoot
中有两种方式实现自定义Filter
,一种是使用 @WebFilter
和 @ServletComponentScan
组合注解,另一种是通过配置类注入FilterRegistrationBean
对象。
2.1 @WebFilter 注解方式:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebFilter { String description() default ""; String displayName() default ""; WebInitParam[] initParams() default {}; String filterName() default ""; String smallIcon() default ""; String largeIcon() default ""; String[] servletNames() default {}; String[] value() default {}; String[] urlPatterns() default {}; DispatcherType[] dispatcherTypes() default {DispatcherType.REQUEST}; boolean asyncSupported() default false; }
几个重要参数详解:
urlPatterns
:自定义需要拦截的URL
,可以使用正则匹配,若没指定该参数值,则默认拦截所有请求filterName
:自定义过滤器的名称initParams
:自定义过滤器初始化参数的数组,此参数可以通过自定义过滤器init()
的入参FilterConfig
对象的getInitParameter()
方法获取;(由于过滤器没有直接排除自定义URL不拦截的设定,如果我们需要在自定义拦截的URL
中排除部分不需要拦截的URL
,可以通过将需要排除的URL
放到initParams
参数中再在doFilter
方法中排除)
// 获取initParams传入的数组参数中所有的参数名: Enumeration<String> initParameterNames = filterConfig.getInitParameterNames(); // 通过参数名获取传入的参数值 String value = filterConfig.getInitParameter("参数名");
如下自定义个一个拦截所有URL
除了带有 /test
的片段名称为testFilter
的过滤器:
@WebFilter(filterName = "testFilter", urlPatterns = "/*", initParams = @WebInitParam(name = "noFilterUrl", value = "/test")) public class TestFilter implements Filter { private List<String> noFilterUrls; @Override public void init(FilterConfig filterConfig) throws ServletException { // 从过滤器配置中获取initParams参数 String noFilterUrl = filterConfig.getInitParameter("noFilterUrl"); // 将排除的URL放入成员变量noFilterUrls中 if (StringUtils.isNotBlank(noFilterUrl)) { noFilterUrls = new ArrayList<>(Arrays.asList(noFilterUrl.split(","))); } } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 若请求中包含noFilterUrls中的片段则直接跳过过滤器进入下一步请求中 String url = ((HttpServletRequest)servletRequest).getRequestURI(); Boolean flag = false; if (!CollectionUtils.isEmpty(noFilterUrls)) { for (String noFilterUrl : noFilterUrls) { if (url.contains(noFilterUrl)) { flag = true; break; } } } if (!flag) { ...... //过滤请求响应逻辑 } filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
在启动类上需要添加@ServletComponentScan
注解才能使过滤器生效。
2.2 FilterRegistrationBean 类方式:
使用自定义配置类注入FilterRegistrationBean
对象配置过滤器。
这种方式和上面哪种方式类似。其实就是将上面那种方式的配置改为创建一个配置类对象,同时也支持配置过滤器执行的先后顺序。如上面举例的过滤器使用该方式配置如下:
@Configuration public class FilterConfig { @Autowired private TestFilter testFilter; @Bean public FilterRegistrationBean requestFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); // 将过滤器配置到FilterRegistrationBean对象中 registration.setFilter(testFilter); // 给过滤器取名 registration.setName("requestFilter"); // 设置过滤器优先级,该值越小越优先被执行 registration.setOrder(1); Map<String, Object> paramMap = new HashMap<>(); paramMap.put("noFilterUrl", "/test"); // 设置initParams参数 registration.setInitParameters(paramMap); List<String> urlPatterns = new ArrayList<>(); urlPatterns.add("/*"); // 设置urlPatterns参数 registration.setUrlPatterns(urlPatterns); return registration; } }
在实现多个过滤器并按照自定义顺序,只需对配置文件注册FilterRegistrationBean
对象中的Order
属性改变顺序。注册多个过滤器,只需要在配置类中创建多个Bean
。
⭐ 过滤器链:
过滤器链是一种责任链模式的设计实现,在一个Filter
处理完成业务后,通过FilterChain
调用过滤器链中的下一个过滤器。
这里需要注意的是,如果实现多个FIlter
功能的过滤器。使用@WebFilter
注解的方式只能根据过滤器名的类名顺序执行,添加@Order
注解是无效的,因为@WebFilter
在容器加载时,不会使用@Order
注解定义的顺序,而是默认直接使用类名排序。所以使用这种方式实现多个过滤器,且有顺序要求,则需要注意类名的定义。