文章目录
- 前言
- 自定义过滤器并验证
- 关于排除某些请求的方式
- 创建测试接口
- 请求测试
- 验证异常过滤器的执行流程
- 注意事项
- 资料参考
前言
在Java-web的开发领域,对于过滤器
和拦截器
用处还是很多,但两者的概念却极易混淆。
过滤器
和拦截器
都是采用AOP
的核心思想,对具体的实现方法
进行增强
,都能拦截请求方法。
不同点 | 过滤器 | 拦截器 |
---|---|---|
研发者 | 由Servlet研发 | 由SpringMVC研发 |
拦截对象 | 拦截WEB请求 | 拦截器不仅可以拦截请求,还能拦截普通方法。 |
执行顺序 | 过滤器会比拦截器优先执行 | 拦截器在过滤器之后执行,但使用比过滤器简单。 |
使用场景 | 编码格式转化 、跨域解决 、xss攻击 等 | 权限控制、日志打印、参数验证、回话信息等。 |
【资料参考】
- 过滤器和拦截器的异同(小计)
- springboot配置监听器、过滤器和拦截器
以实际生活中的栗子,作为各个名词的描述:
- 过滤器 过滤器好比就是筛子,只有满足大小要求的颗粒,才能通过过滤器,不满足的则会筛出来。
- 拦截器 拦截器就好比是一个门户,只能满足要求,才能进入
这两者本身上其实都差不多,只是在技术层面为了区别各个不同的原理和技术,才定义了不同的名词。
本篇博客不说拦截器,只说过滤器的使用和基本准则。
自定义过滤器并验证
创建Springboot项目,并创建两个自定义的过滤器,如下所示:
- MyFilter1
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Slf4j
@Order(1) // 数字越小 优先级越高
@WebFilter(filterName = "myFilter1",urlPatterns = {"/*"})
public class MyFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("#### MyFilter1 ==== init ####");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("#### MyFilter1 ==== doFilter ####");
log.info("#### MyFilter1 ==== doFilter before ####");
chain.doFilter(request,response);
log.info("#### MyFilter1 ==== doFilter after ####");
}
@Override
public void destroy() {
log.info("#### MyFilter1 ==== destroy ####");
}
}
- MyFilter2
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Slf4j
@Order(2) // 数字越小 优先级越高
@WebFilter(filterName = "myFilter2",urlPatterns = {"/*"}) // urlPatterns 设置需要拦截过滤的请求,可以正则表达式,未指定时会拦截所有请求
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("#### MyFilter2 ==== init ####");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("#### MyFilter2 ==== doFilter ####");
log.info("#### MyFilter2 ==== doFilter before ####");
String msg = request.getParameter("msg");
if("err".equalsIgnoreCase(msg)){
throw new RuntimeException("传递参数有误,验证过滤器....");
}
chain.doFilter(request,response);
log.info("#### MyFilter2 ==== doFilter after ####");
}
@Override
public void destroy() {
log.info("#### MyFilter2 ==== destroy ####");
}
}
可以看到本次使用的是注解方式,除了新增这两个自定义的Filter
之外,还需要在启动类上增加一个注解标识,否则自定义的过滤器并不会被加载,如下所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan // 扫描filter
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
@ServletComponentScan
// 扫描filter
在自定义的过滤器中,使用的@WebFilter
注解方式,该注解中需要设定某些值,这些参数信息又有什么含义呢?
参数名 | 含义 |
---|---|
filterName | 给过滤器定义别名称,需要唯一区别。 |
urlPatterns | 过滤器针对的请求url路径,默认或者不配置时,是过滤所有的请求。此处可以写正则表达式。 |
initParams | 自定义过滤器初始化参数的数组,此参数可以通过自定义过滤器 init() 的入参FilterConfig对象的 getInitParameter() 方法获取;(由于过滤器没有直接排除自定义URL不拦截的设定,如果我们需要在自定义拦截的URL中排除部分不需要拦截的URL,可以通过将需要排除的URL放到initParams参数中再在doFilter方法中排除) |
关于排除某些请求的方式
参考:SpringBoot自定义过滤器Filter使用详解
其中,有这么一个栗子,如下所示:
@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 {
// 过滤器配置的 urlPatterns 为 /* 表示过滤所有的请求
// 有请求进来时,会先进入到过滤器中,此时则获取请求的uri地址
String url = ((HttpServletRequest)servletRequest).getRequestURI();
Boolean flag = false;
if (!CollectionUtils.isEmpty(noFilterUrls)) {
for (String noFilterUrl : noFilterUrls) {
// 如果请求的地址中包含有 initParams 传递的参数值,则不继续向下执行
if (url.contains(noFilterUrl)) {
flag = true;
break;
}
}
}
if (!flag) {
...... //过滤请求响应逻辑
}
// 继续向下执行,调用该方法
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
感叹这个作者的奇妙思维方式!
创建测试接口
回归正题,创建上面两个自定义的过滤器之后,还需要创建一个测试接口,如下所示:
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test1")
@Api(tags = "测试控制器 1")
@Slf4j
public class TestController {
@PostMapping("/demo1")
@ApiOperation("demo1")
public String demo1(){
log.info("请求被调用了!!!!!");
return "demo 1 .....";
}
}
启动项目观察控制台打印。
这里的顺序是加在时的顺序,并不是
@Order(int)
设定的原因!
请求测试
清空控制台的日志信息,请求测试接口,观察控制台日志输出情况,如下所示:
通过调用自定义过滤器filter的日志打印,可以很清楚的了解到请求的执行过程。如下所示:
由最开始的客户端请求–》MyFilter1–》MyFilter2–》指定的controller–》MyFilter2–》MyFilter1–》客户端。闭环操作。
这里为什么是MyFilter1
在MyFilter2
之前,根本原因就是定义filter时,设定了@Order(1)
给定了优先级。
数字越小,优先级越高!
验证异常过滤器的执行流程
在MyFilter2
中,给定了请求参数 msg=err
时抛出异常。
出现异常了,后面的filter还会继续执行不?接下来再次进行请求测试:
查看控制台,观察日志信息,如下所示:
在执行到MyFilter1
、MyFilter2
后,由于MyFilter1
中抛出了异常,还没有执行对应的chain.doFilter(request,response);
后续的链路
,所以日志在此处戛然而止!
出现异常后,并不会继续执行后续的filter!
注意事项
自定义的Filter
类上,不要添加 @Component
注解,不然会导致程序启动报错,启动不成功
资料参考
SpringBoot自定义过滤器Filter使用详解
这个大佬博客中给出了第二种方式,但总感觉哪里有问题,关于第二种方式的配置操作,可以参考下列资料:
springboot 自定义过滤器