JavaWeb 学习笔记 7:Filter

本文是JavaWeb学习笔记,介绍了Filter的快速开始、拦截路径、过滤器链,还给出登录验证案例。同时提到Listener是JavaWeb三大组件之一,可监听Application、Session、Request对象的创建、销毁及属性修改等,完整示例可从文中获取。

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() {

    }
}

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

Fitler接口有三个方法:

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

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

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

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>Hello World!</h1>
<% System.out.println("hello.jsp..."); %>
</body>
</html>

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

pre chain.doFilter
hello.jsp...
after chain.doFilter

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

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() {

    }
}

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

before filter1 chain.doFilter
before filter2 chain.doFilter
hello.jsp...
after filter2 chain.doFilter
after filter1 chain.doFilter

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

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() {

    }
}

需要注意的是,这里除了需要通过 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) {
        // 这里执行应用退出时的清理工作
    }
}

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

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

6.参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值