• JavaWeb 学习笔记 7:Filter


    JavaWeb 学习笔记 7:Filter

    1.快速开始

    使用过滤器的方式与 Servlet 类似,要实现一个Filter接口:

    @WebFilter("/*")
    public class FirstFilter implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("pre chain.doFilter");
            // 放行请求
            chain.doFilter(request, response);
            System.out.println("after chain.doFilter");
        }
    
        public void destroy() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里 @WebFilter 指定的是过滤器拦截的路径规则,/*是对所有请求进行拦截。

    Fitler接口有三个方法:

    • init,过滤器初始化时执行
    • doFilter,拦截请求
    • destroy,过滤器销毁时执行

    doFilter方法中,通常需要执行chain.doFilter()方法放行请求,否则请求就不会正常被 Servlet 进行处理,直接被 Filter 阻断。

    添加一个 JSP 以观察 Filter 的执行过程:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
        Title
    
    
    

    Hello World!

    <% System.out.println("hello.jsp..."); %>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    请求这个 JSP 会看到如下输出:

    pre chain.doFilter
    hello.jsp...
    after chain.doFilter
    
    • 1
    • 2
    • 3

    整个过程可以用下图表示:

    Filter工作原理.drawio

    2.Filter 拦截路径

    Filter 可以拦截以下几种路径:

    • 具体路径,比如/jsp/hello.jsp,只有访问这个 JSP 的请求会被拦截。
    • 目录,比如/jsp/*,访问/jsp这个目录下的所有资源路径都会被拦截。
    • 后缀名拦截,比如*.jsp,左右访问后缀名为.jsp的资源都会被拦截。
    • 拦截全部,/*,访问任意资源都会被拦截。

    3.过滤器链

    一个 Web 应用可以配置多个过滤器,这些过滤器合称“过滤器链”。

    下面看实际演示。

    在应用中设置两个过滤器:

    @WebFilter("/*")
    public class Filter1 implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("before filter1 chain.doFilter");
            chain.doFilter(request, response);
            System.out.println("after filter1 chain.doFilter");
        }
    
        public void destroy() {
    
        }
    }
    
    @WebFilter("/*")
    public class Filter2 implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            System.out.println("before filter2 chain.doFilter");
            chain.doFilter(request, response);
            System.out.println("after filter2 chain.doFilter");
        }
    
        public void destroy() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    请求 JSP 页面可以看到如下输出:

    before filter1 chain.doFilter
    before filter2 chain.doFilter
    hello.jsp...
    after filter2 chain.doFilter
    after filter1 chain.doFilter
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果使用注解配置过滤器,过滤器在过滤器链上的先后顺序由过滤器类名做字典排序决定。

    4.案例:登录验证

    使用过滤器可以对一些需要在多个 Servlet 中进行的统一处理进行简化。比如每个 Servlet 都需要的请求内容乱码处理或者响应报文添加统一的报文头等等。

    这里展示如何使用过滤器实现登录验证功能。

    @WebFilter("/*")
    public class LoginFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // 对登录相关请求和静态资源进行放行
            String[] paths = new String[]{
                    "/user/login",
                    "/user/registry",
                    "/user/check_code",
                    "/css/",
                    "/imgs/",
                    "/index.jsp"
            };
            if (!(request instanceof HttpServletRequest &&
                    response instanceof HttpServletResponse)){
                throw new RuntimeException("This is not web Application.");
            }
            HttpServletRequest hRequest = (HttpServletRequest) request;
            HttpServletResponse hResponse = (HttpServletResponse) response;
            String url = hRequest.getRequestURL().toString();
            String prefix = "http://"+hRequest.getHeader("host")+hRequest.getContextPath();
            for (String path: paths){
                if (url.indexOf(prefix+path) == 0){
                    // 符合规则,放行
                    System.out.println("放行"+url);
                    chain.doFilter(hRequest, hResponse);
                    return;
                }
            }
            // 检查是否登录,如果没有登录,重定向到登录页面
            Object username = hRequest.getSession().getAttribute("username");
            if (username == null){
                hResponse.sendRedirect("/login-demo/user/login");
                return;
            }
            // 已经登录,放行
            chain.doFilter(hRequest, hResponse);
    
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    需要注意的是,这里除了需要通过 Session 判断是否登录状态,没有登录的让重定向到登录页面以外,还需要对于特殊路径进行放行,否则有些页面就无法正常显示。在我们这个示例中,需要非登录状态就可以访问的资源有:

    • CSS
    • 图片
    • 登录相关的 Servlet
    • 注册相关的 Servlet
    • 返回验证码的 Servlet
    • 首页(用于重定向到登录页)

    对于这些特殊资源,这里简单地用字符串匹配的方式判断和处理。

    5.Listener

    Listener、Filter 和 Servlet 是 JavaWeb 的三大组件。

    Listener 的用途是监听 Application\Session\Request 三个对象的创建、销毁或属性的修改和删除。

    具体包含以下监听器:

    image-20230912222649088

    利用 ServletContext监听器,我们可以在 Servlet 容器创建后执行一些应用的初始化代码:

    @WebListener
    public class ApplicationRunner implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            // 这里执行一些应用的初始化工作
            System.out.println("Web Application is already run...");
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            // 这里执行应用退出时的清理工作
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用监听器很容易,只要实现相应的接口,并使用@WebListener注解标记类即可。

    本文的完整示例可以从这里获取。

    6.参考资料

  • 相关阅读:
    报时机器人的rasa shell执行流程分析
    lambda表达式
    Flutter:动画摘要
    基于html的美食网站 奶茶网页设计与实现(HTML+CSS+JavaScript)
    DockerFile 文件详解、docker-maven-plugins的使用的使用
    fiddler如何抓模拟器中APP的包
    Elasticsearch 入门 索引、分词器
    Environment_Variable_and_SetUID
    Unity的BuildPlayerProcessor:深入解析与实用案例
    Day658.JDK8重要特性 -Java业务开发常见错误
  • 原文地址:https://blog.csdn.net/hy6533/article/details/133160973