SpringBoot2.X 实战3 -- Filter
一.前言
1.Filter简介
Filter具有这样的能力:它能够对资源(servlet或静态内容)的请求上执行过滤任务,或者在资源的响应上执行过滤任务,或者两者都执行。过滤器在doFilter方法中执行过滤。 每个Filter都可以访问FilterConfig对象,从该对象可以获取其初始化参数,即可以使用的ServletContext引用,例如,加载过滤任务所需的资源。过滤器是在web应用程序的部署描述符中配置的。
ServletRequest 会在到达 Servlet 之前,先接受到 ServletRequest 请求;
在这里可以根据需要检测 ServletRequest 头信息也可以修改响应的头信息,如检测用户的 IP 是否在白名单里面,打印日志等;
在 ServletResponse 到达用户之前进行数据检测和修改;
2.Filter 种类很多如:
a.权限管理类的 Filter: 负责检查用户是否有权限访问当前资源(SpringSecurity);
b.ErrorPageFilter:它注册错误页面,并通过过滤请求并转发到错误页面来处理应用程序错误,而不是让服务器处理它们。错误页面是servlet规范的一个特性,但是没有用于在规范中注册它们的Java API。
c.LoggerContextFilter:简单的请求日志记录过滤器,它将请求URI(或查询字符串)写入公有日志。
二.Filter 运行流程
1.Filter 对用户的请求进行与预处理;
2.将请求交给 Servlet 处理并生成响应;
3.Filter 对服务器响应进行后处理.
三.Filter接口方法
写自己的 Filter 需要实现 javax.servlet.Filter 接口并覆写其中的三个方法:
1.init 方法:
该方法由 web 容器调用,以指示 Filter 已经投入使用。servlet容器在实例化过滤器之后调用init方法一次。在要求过滤器执行任何过滤工作之前,init方法必须成功完成。当 init 方法出现以下情况时 Filter 将不生效:
a.抛出了 ServletException 异常;
b.在一段时间内没有返回结果(超时了).
2.destroy方法:
由web容器调用,以指示 Filter 它正在从服务中取出。此方法只在过滤器的doFilter方法中的所有线程退出或超时时间过后调用。在web容器调用此方法之后,它将不会在过滤器的这个实例上再次调用doFilter方法。此方法使 Filter 有机会清除所有正在保留的资源(例如,内存,文件句柄,线程),并确保任何持久状态与过滤器在内存中的当前状态同步。
3.doFilter方法
这个是整个过滤器中最重要的方法.该部分是使用的责任链的设计模式。责任链设计模式中使用了一个钩子指向下一个过滤器(filterChain.doFilter()方法指向下一个过滤器)每当请求/响应对通过链时,容器都会调用过滤器的 doFilter 方法,这是由于客户端在链末端对资源的请求。传递给此方法的FilterChain允许过滤器将请求和响应传递给链中的下一个实体。
此方法的典型实现将遵循以下原则:
a.检查请求
b.可以选择用自定义实现包装请求对象,以过滤用于输入过滤的内容或标题;
c.可以选择用自定义实现包装响应对象,以过滤用于输出过滤的内容或标题;
d. 使用FilterChain对象(filterChain.doFilter())调用链中的下一个实体或者不将请求/响应对传递给过滤器链中的下一个实体以阻止请求处理;
e.在调用过滤器链中的下一个实体后,直接在响应上设置标头。
四.自定义 Filter
根据第三小节描述,首先继承 javax.servlet.Filter 并实现三个方法:
1.基本用法
@WebFilter 标识该过滤器的一些基本信息:
filterName:过滤器名称;
urlPatterns = "/filter1" :过滤的路由
@Slf4j
@WebFilter(filterName = "filter1", urlPatterns = "/filter1")
public class RestFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("RestFilter1 初始化了");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
log.info("RestFilter1 请求处理");
String result = request.getReader().lines().reduce("", String::concat);
System.out.println("result:" + result.replace("\t",""));
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
log.info("RestFilter1 销毁");
}
}
RestController 代码
@PostMapping("/filter1")
public String postString1(){
return "post-filter1";
}
当我们请求接口时能获得如下结果:
1屏幕快照 2018-08-07 下午8.42.png
2.带执行顺序的过滤器
只要在类上加@Order(1)注解(org.springframework.core.annotation.Order)。
数字越小执行顺序越靠前。
@Slf4j
@Order(0)
@WebFilter(filterName = "filter1", urlPatterns = "/filter1")
public class RestFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("RestFilter1 初始化了");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
log.info("RestFilter1 请求处理");
String result = request.getReader().lines().reduce("", String::concat);
System.out.println("result:" + result.replace("\t",""));
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
log.info("RestFilter1 销毁");
}
}
3.在过滤器执行期间直接返回结果
使用场景:用户的 IP 在黑名单里
@Slf4j
@Order(1)
@WebFilter(filterName = "filter2", urlPatterns = "/filter2")
public class RestFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("RestFilter2 初始化了");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String result = request.getReader().lines().reduce("", String::concat);
response.getWriter().write(result.replace("\t",""));
}
@Override
public void destroy() {
}
}
这时直接返回请求体内的数据:
RestController 代码:
@PostMapping("/filter2")
public String postString2(){
return "post-filter2";
}
请求达到的结果:
2屏幕快照 2018-08-07 下午8.41.png