SpringBoot_Filter的详解

shihaiming 阅读:59 2022-07-29 11:52:58 评论:0

SpringBoot&Filter的详解

过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理

大概流程图如下

应用场景

  • 自动登录

  • 统一设置编码格式

  • 访问权限控制

  • 敏感字符过滤等

  • ......


1、Filter的使用

  要想使用filter,需要写一个方法继承Filter类,我们写如下两个自己的Filter类,首先是FirstFilter类,其中@Order里边的数字越小代表越先被该Filter过滤,@WebFilter代表这是个Filter类并把这个类注入到容器中。

注意:

    SpringBoot 实现 Filter 两种方式:

  1. 自定义Filter通过FilterRegistrationBean 类来注入;(不推荐)

  2. 自定义Filter通过 实现接口 Filter方法,@WebFilter注入。

FirstFilter

@Order(1) 
@WebFilter(filterName="firstFilter", urlPatterns="/*") 
public class FirstFilter implements Filter { 
 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
 
    } 
 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        System.out.println("first filter 1111111111111111111"); 
        chain.doFilter(request, response); 
        System.out.println("first filter 2222222222222222222"); 
    } 
 
    @Override 
    public void destroy() { 
 
    } 
}

SecondFilter

@Order(2) 
@WebFilter(filterName="secondFilter", urlPatterns="/*") 
public class SecondFilter implements Filter { 
 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
 
    } 
 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        System.out.println("second filter 1============================="); 
        System.out.println("before:" + response); 
        chain.doFilter(request, response); 
        System.out.println("after:" + response); 
        System.out.println("second filter 2============================="); 
 
    } 
 
    @Override 
    public void destroy() { 
 
    } 
}

TestController

@RestController 
public class TestController { 
 
    @GetMapping("/test1") 
    public String test1() { 
        System.out.println("method in controller"); 
        return "test1"; 
    } 
 
}

postman测试:http://localhost:8080/test1

输出结果:

first filter 1111111111111111111 
second filter 1============================= 
before:org.apache.catalina.connector.ResponseFacade@1d2aafd8 
method in controller 
after:org.apache.catalina.connector.ResponseFacade@1d2aafd8 
second filter 2============================= 
first filter 2222222222222222222

过程:

总结:

  我们可以看出代码执行的流程,首先请求被firstfilter截获,打印出first filter 1,然后去执行chain.doFilter(request, response),这句话代表着请求会转发给过滤器链上下一个对象,也就是secondfilter,所以打印出secondfilter里的second filter 1,接下来再执行secondfilter里的chain.dofilter()方法,请求再转发给下一个对象,由于没有其他的filter了,所以会转发给controller,打印出了controller类中的method in controller,接下来再去内存栈里调用secondfilter的print("second filter 2"),然后再去内存栈里调用firstfilter的print("first filter 1")。所以如果在自己实现的Filter类的doFilter方法里不加chain.doFilter(req, rep)是万万不行的,那样会导致请求到了这个filter里就不再往下走了,永远进不了controller中。

  我们也可以在print("before:" + response)和print("after:" + response)这两个地方打上断点,然后调试一下,你会发现在before那里的response里是什么都么有的,而在after那里的response里则是已经有了test1字符串,也就是说controller类test1方法的返回值已经添加进了response,所以如果你想对请求的response做一下过滤处理,那么一定要在chain.doFilter(res, rep)之后写你的逻辑。


2、@WebFilter注解说明

  @WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )

@WebFilter 的常用属性

属性名 类型 说 明
filterName String 指定过滤器的 name 属性,等价与 <filter-name> 标签
value String[] 该属性等价于 <url-pattern> 标签,但是两者不应该同时使用
urlPatterns String[] 指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签。
servletNames String[] 用于指定过滤的servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 <servlet-name> 的取值。
dispatcherTypes DispatcherType[] 指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST
initParams WebInitParam[] 用于设置该过滤器类的一些初始化参数,等同于<init-param> 标签
asyncSupported Boolean 声明过滤器是否支持异步操作模式,等价于 <async supported> 标签
description String 对该过滤器的描述信息,等价于 <description> 标签
displayName String 过滤器显示名,通常配合工具使用,等同与<display-name> 标签

3、责任链模式的实际应用

Filter和FilterChain都是怎么用责任链模式实现的,自定义实现一下, 使用Filter模式来模拟

Filter接口

public interface Filter { 
    /** 
     * 过滤器 
     * @param request 请求对象 
     * @param response 返回对象 
     */ 
    public void doFilter(Request request, Response response, FilterChain chain); 
 
}

FilterChain类 { 过滤器链 }

/** 
 * @Desc: TODO 过滤器链【执行责任链模式的主要成员】 
 */ 
public class FilterChain implements Filter { 
 
    /** 
     * 过滤器列表 
     */ 
    private List<Filter> filters = new ArrayList<>(); 
 
    int index = 0; 
 
    /** 
     * 添加过滤器 
     * @param filter 
     */ 
    public FilterChain addFilter(Filter filter) { 
        filters.add(filter); 
        return this; 
    } 
 
    @Override 
    public void doFilter(Request request, Response response, FilterChain chain) { 
        if(index == filters.size()) { 
            /** 
             * 真正处理请求(此时开始处理 controller业务逻辑) 
             */ 
            return; 
        } 
        Filter filter = filters.get(index); 
        index++; 
        filter.doFilter(request, response, chain); 
    } 
 
}

Request类和Response类

public class Request { 
    public String requestStr; 
}
public class Response { 
    public String responseStr; 
}

实现类(业务中需要的过滤器,以下举例)

EncodeFilter{ 设置统一编码 }

public class EncodeFilter implements Filter { 
    @Override 
    public void doFilter(Request request, Response response, FilterChain chain) { 
        request.requestStr = request.requestStr + "  设置编码 UTF-8"; 
        System.out.println("EncodeFilter request Str:" + request.requestStr); 
        chain.doFilter(request, response, chain); 
        response.responseStr = response.responseStr + "-------------设置编码 UTF-8"; 
        System.out.println("EncodeFilter response Str:" + response.responseStr); 
    } 
}

VerifyParameterFilter{ 参数校验 }

public class VerifyParameterFilter  implements Filter { 
    @Override 
    public void doFilter(Request request, Response response, FilterChain chain) { 
        request.requestStr = request.requestStr + "  参数验证 成功"; 
        System.out.println("VerifyParameterFilter request str:" + request.requestStr); 
        chain.doFilter(request, response, chain); 
        response.responseStr = response.responseStr + "======================"; 
        System.out.println("VerifyParameterFilter response str:" + response.responseStr); 
    } 
}

测试

public class FilterTest { 
    public static void main(String[] args) { 
        String msg = "张三"; 
        Request request = new Request(); 
        request.requestStr = msg; 
        Response response = new Response(); 
        response.responseStr = "success *****"; 
 
        FilterChain fc = new FilterChain(); 
        fc.addFilter(new EncodeFilter()) 
                .addFilter(new VerifyParameterFilter()) 
                .doFilter(request, response, fc); 
    } 
}

结果:

EncodeFilter request Str:张三  设置编码 UTF-8 
VerifyParameterFilter request str:张三  设置编码 UTF-8  参数验证 成功 
VerifyParameterFilter response str:success *****====================== 
EncodeFilter response Str:success *****======================-------------设置编码 UTF-8

总结

  1. Filter是针对请求进行拦截、意在请求前进行一些过滤、权限校验、日志记录、统一编码等

  2. 所有Filter统一在FilterChain中组成一个链条,然后调用也统一由FilterChain来协调,确保以链条的模式执行

  3. 责任链模式是一种重要的设计模式,如Servlet中的Filter模式、Mybatis中的Plugin模式等都是责任链模式的体现


本文参考链接:https://www.cnblogs.com/mmdz/p/15475615.html
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

搜索
排行榜
关注我们

一个IT知识分享的公众号