关于过滤器(Filter)的学习

发布于:2025-08-08 ⋅ 阅读:(13) ⋅ 点赞:(0)

过滤器(Filter)概述

         过滤器是 Java Servlet 规范的一部分,用于在请求到达 Servlet 之前或响应返回客户端之前拦截请求和响应。它可以用于执行各种任务,如请求预处理、响应后处理、身份验证、日志记录等。

过滤器的作用

  • 预处理请求 :在请求到达目标资源(如 Servlet、JSP)之前,对请求进行处理,如设置字符编码、验证用户身份等。
  • 响应后处理响应 :在目标资源处理完请求后,对响应进行处理,如修改响应内容、设置响应头等。
  • 统一处理逻辑 :将一些通用的处理逻辑集中到过滤器中,避免在多个 Servlet 或 JSP 中重复编写相同的代码,提高代码的可维护性和复用性。
  • 控制请求访问 :根据特定条件(如用户角色、请求路径等)决定是否允许请求继续转发到目标资源,实现访问控制。

过滤器的实现步骤

  1. 导包 :确保项目中正确导入了 Servlet API 相关的 JAR 包,这些包通常包含在 Servlet 容器(如 Tomcat)的库文件中。

  2. 编写过滤器类 :创建一个 Java 类,实现 javax.servlet.Filter​ 接口,并重写其三个核心方法:

    • ​init(FilterConfig filterConfig)​ :用于初始化过滤器,当 Web 服务器启动时,会自动调用该方法。可以在这个方法中获取初始化参数、进行资源的加载等操作。例如,图片中的代码在 init​ 方法中输出了过滤器初始化的信息。
    • doFilter(ServletRequest request, ServletResponse response, FilterChain chain)​ :这是过滤器的核心方法,用于拦截请求和响应。在这个方法中,可以对请求进行预处理,然后通过调用 chain.doFilter(request, response)​ 方法将请求传递给下一个过滤器或目标资源,最后进行后处理。图片中的示例代码在 doFilter​ 方法中设置了请求和响应的字符编码,并在请求处理前后分别输出了相应信息。
    • destroy()​ :用于销毁过滤器,当 Web 服务器关闭时,会调用该方法。可以在这个方法中释放资源等操作。图片中的代码在 destroy​ 方法中输出了过滤器销毁的信息。

过滤器的配置

过滤器需要在 web.xml​ 文件中进行配置,或者使用注解的方式进行配置(Servlet 3.0 及以上版本支持)。配置的主要内容包括:

  • 定义过滤器 :使用 <filter>​ 元素定义过滤器的名称(<filter-name>​)和实现类(<filter-class>​)。例如,图片中的 web.xml​ 配置代码定义了一个名为 CharacterEncodingFilter​ 的过滤器,其对应的类为 com.kuang.filter.CharacterEncodingFilter​。
  • 配置过滤器映射 :使用 <filter-mapping>​ 元素指定过滤器所拦截的请求的 URL 模式(<url-pattern>​)或 Servlet 名称(<servlet-name>​)。这样,当匹配到相应的请求时,过滤器就会被调用。图片中的示例配置了过滤器的映射,使其拦截所有以 /servlet/​ 开头的请求。

过滤器的执行顺序

  • 如果有多个过滤器同时拦截同一个请求,它们的执行顺序是由过滤器的映射配置决定的。在 web.xml​ 中,过滤器的映射配置顺序决定了它们的执行顺序,先配置的过滤器先执行。
  • 在请求的预处理阶段,过滤器按照配置顺序依次执行;在响应的后处理阶段,则按照相反的顺序执行。

图解

📌 示例一:字符编码设置过滤器

✅ 1. 过滤器类:filterDemo.java​
public class filterDemo implements Filter {
    // 定义一个私有变量,用于存储要设置的编码格式,默认值为 "UTF-8"。
    private String encoding = "UTF-8";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
       System.out.println("过滤器初始化");
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        response.setContentType("text/html;charset=" + encoding);
        chain.doFilter(request, response);
    }
}

✅ 2. 配置:web.xml​
  <servlet>
    <servlet-name>TestDemo</servlet-name>
    <servlet-class>www.TestDemo.TestDemo</servlet-class>
  </servlet>
  <servlet-mapping>
          <servlet-name>TestDemo</servlet-name>
          <url-pattern>/Test/TestDemo</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>TestDemo</servlet-name>
    <url-pattern>/TestDemo</url-pattern>
  </servlet-mapping>

    <filter>
        <filter-name>filterDemo</filter-name>
        <filter-class>www.TestDemo.filterDemo</filter-class>
    </filter>
    <filter-mapping>
            <filter-name>filterDemo</filter-name>
            <url-pattern>/TestDemo</url-pattern>
    </filter-mapping>

       过滤器路径为/TestDemo,如果路径走localhost:8080/FilterDemo_war/TestDemo就会处理中文乱码,路径走localhost:8080/FilterDemo_war/Test/TestDemoz则会显示中文乱码;

📌 示例二:动态生成个性化欢迎语

【LoginServlet.java】

package www.Demo02;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

// 登录请求处理 servlet
public class LoginServlet extends HttpServlet {

    // 专门处理 POST 请求(表单 method="post")
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String isVipStr = req.getParameter("isVip");

        // 如果复选框被勾上,浏览器会带 isVip=true;否则不带;把字符串 "true" 转成布尔值 true/false
        boolean isVip = "true".equals(isVipStr);

        // 硬编码的用户名密码校验
        if ("admin".equals(username) && "123456".equals(password)) {
            // 校验成功,把用户名存到 session
            req.getSession().setAttribute("username", username);
            // 把是否是 VIP 也存到 session(boolean 自动装箱为 Boolean)
            req.getSession().setAttribute("isVip", isVip);
            // 重定向到成功页面
            resp.sendRedirect("/FilterDemo_war/sys/success.jsp");
        } else {
            // 登录失败,重定向到错误页
            resp.sendRedirect("/FilterDemo_war/sys/error.jsp");
        }
    }
    // 如果浏览器用 GET 访问,也按 POST 方式处理
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doPost(req, resp);
    }
}

【VipFilter.java】

package www.Demo02;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
// 过滤器:给登录成功的用户动态添加欢迎语
public class VipFilter implements Filter {
    // 过滤器被容器创建后,仅执行一次,可用来读初始化参数
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("VipFilter init");
    }

    // 每次请求都会经过这里
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // 把父接口转成 HttpServletRequest,才能用 getSession()
        HttpServletRequest req = (HttpServletRequest) request;
        // 参数 false:若 session 不存在,不自动创建而是返回 null
        HttpSession session = req.getSession(false);

        // 1. 判断用户是否已登录(session 存在且里面有 username)
        if (session != null && session.getAttribute("username") != null) {
            // 2. 取出登录时存的 isVip 属性
            Boolean isVip = (Boolean) session.getAttribute("isVip");
            if (isVip == null) {        // 容错:万一没存,就当成非 VIP
                isVip = false;
            }

            // 3. 根据是否 VIP 准备不同的欢迎语
            String welcomeMessage;
            if (isVip) {
                welcomeMessage = "尊贵的VIP用户,欢迎您的登录";
            } else {
                welcomeMessage = "欢迎您的登录";
            }

            // 4. 把欢迎语放到 request 域,给 success.jsp 用
            req.setAttribute("welcomeMessage", welcomeMessage);
        }
        // 5. 必须继续往下走,否则请求会被“卡死”在过滤器里
        chain.doFilter(request, response);
    }

    // 容器卸载过滤器时调用,一般做资源清理
    @Override
    public void destroy() {
        System.out.println("VipFilter destroy");
    }
}

【login.jsp】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<h2>用户登录</h2>
<!-- 表单提交到 /项目名/LoginDemo,由 LoginServlet 处理 -->
<form action="${pageContext.request.contextPath}/LoginDemo" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    是否为会员:<input type="checkbox" name="isVip" value="true"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

【success.jsp】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <!-- 过滤器在 request 域里放的欢迎语 -->
    <h1>${requestScope.welcomeMessage}</h1>
    <!-- session 域里放的用户名 -->
    <h3>${sessionScope.username}!</h3>
</body>
</html>

【web.xml】

<!-- 项目测试2 -->
<servlet>
    <servlet-name>LoginServlet</servlet-name>
    <servlet-class>www.Demo02.LoginServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>LoginServlet</servlet-name>
    <url-pattern>/LoginDemo</url-pattern>
</servlet-mapping>

<!-- 注册 VipFilter -->
<filter>
    <filter-name>VipFilter</filter-name>
    <filter-class>www.Demo02.VipFilter</filter-class>
</filter>
<!-- 凡是访问 /sys/* 路径的请求,都要先过 VipFilter -->
<filter-mapping>
    <filter-name>VipFilter</filter-name>
    <url-pattern>/sys/*</url-pattern>
</filter-mapping>

<!-- 全局 session 超时时间:1 分钟(仅做演示) -->
<session-config>
    <session-timeout>1</session-timeout>
</session-config>
示例2总结:
  1. 登录:login.jsp → LoginServlet,验证 admin/123456 并把用户名、是否 VIP 写进 session。
  2. 过滤:所有访问 /sys/*​ 的请求先走 VipFilter,根据 session 给 request 加上欢迎语。
  3. 展示:success.jsp 读取 request 的欢迎语 + session 的用户名,完成页面渲染

网站公告

今日签到

点亮在社区的每一天
去签到