SpringSpringHome

SpringBoot2.X 实战3 -- Filter

2018-08-07  本文已影响9人  聆世冷暖

一.前言

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

五.代码路径

https://github.com/shaopro/SpringBootFilter

上一篇下一篇

猜你喜欢

热点阅读