1、为什么需要Filter
在日常的开发中,我们的项目可能会被各种各样的客户端进行访问,那么,一些带有意图的朋友,就会利用自己所学的技术进行有目的的访问,那么我们的服务端就不再安全和可靠,我相信每位开发者都知道爬虫这种东西,那么当我们的请求不再安全,那么我们后台的数据就会变得透明。
数据透明,是一件多么可怕的事情,在这个数字潮流时代,数据就是金钱,在生活中任何一个系统都会录入我们的个人信息。
那么对请求进行过滤、请求的校验就变得尤为重要。
2、常用的Filter方式
- 在很久以前的Servlet项目中,可以使用@WebFilter注解来进行Filter的配置。
- 在目前SpringBoot作为后端主流框架而言,使用更多的是配置FilterRegistrationBean类,本文也主要以此类来配置Filter。
两种方式都是针对于Filter接口的实现类而言的。
3、Filter接口
一般我们实现Filter接口,只需要实现doFilter方法即可,但是也可以实现另外两个方法。
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
4、FilterRegistrationBean类
可以看到此类的内部需要一个T类型的filter属性,而这个属性也是FilterRegistrationBean的核心,后面我们只需要将自定义的Filter放入到不同的FilterRegistrationBean中就可以了。
public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {
private T filter;
public FilterRegistrationBean() {
super(new ServletRegistrationBean[0]);
}
public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
super(servletRegistrationBeans);
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
public T getFilter() {
return this.filter;
}
public void setFilter(T filter) {
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
}
5、自定义Filter代码实现
5.1、自定义Filter
自定义的Filter不用使用@Bean进行注入
5.1.1、UserFilter拦截对用户信息的请求
public class UserFilterConfig implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("用户过滤器触发成功");
// 核心代码省略
filterChain.doFilter(servletRequest,servletResponse);
}
}
5.1.2、AuthFilter拦截基本的认证信息
public class AuthFilterConfig implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("认证过滤器触发成功");
// 核心代码省略
filterChain.doFilter(servletRequest,servletResponse);
}
}
5.2、配置FilterRegistrationBean类
对于不同的Filter对象需要配置不同的FilterRegistrationBean类,因为存在重复代码,所以我进行了代码提取,并且向容器中注入相应的对象。
在此配置类中我使用到了Builder这种方式来进行数据的配置,这种方式在当前的SpringBoot框架中是非常常见的,这种方式也非常的好用,值得学习。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<UserFilterConfig> userFilterConfigFilterRegistrationBean(){
FilterRegistrationBean<UserFilterConfig> userFilter = new FilterRegistrationBean<>();
Builder<UserFilterConfig> userBuilder = new Builder<>(userFilter);
userBuilder.filterConfiguration(UserFilterConfig.class,1,false,"/*");
return userFilter;
}
@Bean
public FilterRegistrationBean<AuthFilterConfig> authFilterConfigFilterRegistrationBean(){
FilterRegistrationBean<AuthFilterConfig> authFilter = new FilterRegistrationBean<>();
Builder<AuthFilterConfig> authBuilder = new Builder<>(authFilter);
authBuilder.filterConfiguration(AuthFilterConfig.class,6,false,"/test/*");
return authFilter;
}
private class Builder<T extends Filter>{
private FilterRegistrationBean<T> filterRegistrationBean = null;
public Builder(FilterRegistrationBean<T> filterRegistrationBean){
this.filterRegistrationBean = filterRegistrationBean;
}
public Builder filterConfiguration(Class<? extends Filter> clazz,int order,boolean async,String ...patterns){
T filter = null;
try {
filter = (T)clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
System.out.println("[ " + clazz.toString() + " ] 过滤器对象不存在");
}
this.filterRegistrationBean.setFilter(filter); // 设置过滤器
this.filterRegistrationBean.setOrder(order); // 设置启动顺序
String clazzPath = clazz.toString().toLowerCase(Locale.ROOT);
// 配置过滤器的名称,首字母一定要小写,不然拦截了请求后会报错
this.filterRegistrationBean.setName(clazzPath.substring(clazzPath.lastIndexOf(".")));
this.filterRegistrationBean.addUrlPatterns(patterns); // 配置拦截的请求地址
return this;
}
}
}
6、运行结果
6.1、Controller类如下:
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class FilterDemoController {
private final ApplicationContext applicationContext;
@GetMapping(value = "/abc")
public void show(){
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
if(beanDefinitionName.contains("Filter")){
System.out.println(beanDefinitionName);
}
}
System.out.println("=====> end <=====");
}
@GetMapping(value = "/test/abc")
public void test(){
}
}
/abc:会打印当前容器中所有的Filter对象。
/test/abc:什么也不做。
6.2、控制台显示
当我访问http://localhost:8080/abc
时,就会触发UserFilter这个过滤器,结果如下:
可以看到,过滤器会先触发,然后打印出所有的Filter,容器中会存在两个不同的FilterRegistrationBean。
当我访问http://localhost:8080/test/abc
时,就会触发AuthFilter这个过滤器,结果如下:
文章来源:https://www.toymoban.com/news/detail-465703.html
耶??为啥结果不是想象的那样??
这是因为我的UserFilter的拦截路径为/*
,而AuthFilter的拦截路径为/test/*
。
那为什么UserFilter会在AuthFilter之前执行呢?
因为/*
的拦截范围比/test/*
的范围大,可以说/test/*
是经过了/*
拦截过再进行了匹配拦截。于此同时,我在相应的FilterRegistrationBean中也设置了Filter的执行顺序。文章来源地址https://www.toymoban.com/news/detail-465703.html
7、总结
- 使用Builder这种方式对配置类中的数据进行配置,是当前许多框架都在使用的方式,能够在一定程度上隐藏内部的实现。
- FilterRegistrationBean类提供了自定义FIlter的执行顺序,上文的Demo中因为拦截的范围问题,所以不容易看出存在执行顺序的问题,但是想要看到顺序问题也非常的简单,重新给setOrder方法赋值就行了,优先级低的先执行。
到了这里,关于SpringBoot很实用的请求过滤器 - FilterRegistrationBean的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!