15. JavaWeb 三大组件之 过滤器 Filter


15.1 官方文档

15.1.1 文档:java_ee_api_中英文对照版.chm

15.2 Filter 过滤器说明

15.2.1 为啥要过滤器-需求示意图

  • 一图胜千言

在这里插入图片描述


15.2.2 过滤器介绍

  1. Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
  2. Filter 过滤器是 JavaEE 的规范,是接口

在这里插入图片描述

  1. Filter 过滤器它的作用是:拦截请求,过滤响应

  2. 应用场景

    ● 权限检查
    ● 日记操作
    ● 事务管理

15.3 Filter 过滤器基本原理

  • 一图胜千言

在这里插入图片描述

15.4 Filter 过滤器快速入门

  • 需求: 在 web 工程下,有后台管理目录 manage
  • 要求该目录下所有资源 (html、图片、jsp 、Servlet 等)用户登录后才能访问

在这里插入图片描述


在这里插入图片描述


  • 思路分析-程序框架图,帮助理解,编程小技巧
  • 作用:清晰思路.=> 慢慢通过这样方式来锻炼自己的编程思维!!

在这里插入图片描述

说明, 完成模块的套路/流程:

  1. 先完成一个正确的流程-看到一个效果-> 写后面代码就可以验证.
  2. 加入其它的功能[1. 加入 session,验证合法性]
  3. 完善功能
  • 代码实现
  1. 创建 web\login.jsp ,注意路径,登录界面不应收到过滤器的影响,否则用户将没有机会进行正确登录流程
<%--
  Created by IntelliJ IDEA.
  User: 谢家升
  Date: 2022/3/16
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>管理后台登录</title>
</head>
<body>
<h1>管理后台登录</h1>
<form action="<%=request.getContextPath()%>/loginCheckServlet" method="post">
    u: <input type="text" name="username"/> <br/><br/>
    p: <input type="password" name="password"/> <br/><br/>
    <input type="submit" value="用户登录">
</form>
</body>
</html>

  1. 创建 LoginCLServlet.java
package com.xjs.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Year;

/**
 * @Author: 谢家升
 * @Date: 2022/3/16-03-16-16:17
 * @Version: 1.0
 */
public class LoginCheckServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取用户名和密码
        //假设 密码是 123456 ,就认为是合法用户
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("123456".equals(password)) {
            //合法,将用户名加入session
            request.getSession().setAttribute("username", username);

            //合法,请求转发到 admin.jsp
            request.getRequestDispatcher("/manage/admin.jsp").forward(request, response);
        } else {
            //不合法,重定向到 login.jsp  路径:/filter/login.jsp
            response.sendRedirect(request.getContextPath() + "/login.jsp");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

  1. 创建 web\manage\admin.jsp 【特别提醒文件路径】
<%--
  Created by IntelliJ IDEA.
  User: 谢家升
  Date: 2022/3/16
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>后台管理</title>
    <base href="<%=request.getContextPath()%>/manage/"/>
</head>
<body>
<h1>后台管理</h1>
<%
    //验证request对象是和前面的filter的 servletRequest 是同一个对象
    System.out.println("request= " + request);
%>
<a href="#">用户列表</a> || <a href="#">添加用户</a> || <a href="#">删除用户</a> <br/>
<hr/>
<img src="3.png" >
</body>
</html>

  1. 创建 ManageFilter.java
package com.xjs.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/3/16-03-16-16:48
 * @Version: 1.0
 */

/**
 * 解读:
 * 1.在web工程启动的时候,filter由Tomcat创建实例,默认只会创建一个
 * 2.会调用filter默认会调用无参构造器,同时会调用init()方法,只会调用一次
 * 3.在创建filter实例时,同时会创建 FilterConfig对象,并通过 init()方法传入
 * 4.通过FilterConfig对象,程序员可以获取该filter的相关配置信息
 * 5.当一个http请求和该filter的 url-patter 匹配时,就会调用 doFilter()方法
 * 6.在调用doFilter方法时,Tomcat会同时创建 ServletRequest和 ServletResponse 和 FilterChain对象
 *   ,并通过doFilter方法传入
 * 7.如果后面的请求目标资源(jsp, servlet ...) 会使用到request 和response对象,那么会继续传递
 */
public class ManageFilter implements Filter {

    private int count;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //当Tomcat 创建 filter后,会调用该方法进行初始化
        //回忆我们自己实现tomcat底层机制 + servlet程序,就了然了
        System.out.println("ManageFilter init() 被调用~~");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //当每次调用该filter时,doFilter方法就会被调用
        System.out.println("ManageFilter doFilter() 被调用..." + (++count));

        //如果这里,没有调用继续请求的方法,则就停止

        //如果继续访问目标资源,---> 等价于放行
        //特别说明:在调用过滤器之前,request对象已经被创建并封装
        //所以:我们这里就可以通过 servletRequest 获取很多信息,
        // 比如 url,session,访问的参数...就可以做事务管理,数据获取,日志管理

        //获取session
        //后面还可以继续使用 httpServletRequest 的相关方法
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpSession session = httpServletRequest.getSession();
        //获取 username session对象,后面还可以继续使用
        Object username = session.getAttribute("username");
        if (username != null) {
            //登录成功过,放行
            //解读:filterChain.doFilter(servletRequest, servletResponse);
            //1. 继续访问目标资源url
            //2. servletRequest 和 servletResponse 对象会传递给目标资源/文件
            //3. 一定要理解,filter传递的两个对象,在后面的servlet/jsp 是同一个对象(指的是在一次http请求中)
            System.out.println("servletRequest= " + servletRequest);
            System.out.println("=====日志信息=====");
            System.out.println("访问的用户名:" + username.toString());
            System.out.println("访问的url= " + httpServletRequest.getRequestURL());
            System.out.println("访问的IP= " + httpServletRequest.getRemoteAddr());
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            //没有登录过,回到登录页面
            servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
        }


    }

    @Override
    public void destroy() {
        //当filter被销毁时,会调用该方法
        System.out.println("ManageFilter destroy() 被调用...");

    }
}

  1. 在 web.xml 配置过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">



    <!--解读:filter一般写在其他servlet的前面
            1.filter 的配置和servlet 非常相似,filter也是被Tomcat管理和维护的
            2.url-pattern 就是 当请求资源的 url 和其匹配的时候,就会调用该filter
            3./manage/* 第一个 / 会被解析成 http://ip:port/工程路径/
            4.完整的路径就是 http://ip:port/工程路径/manage/* 当请求的资源 url满足该路径时
            都会调用filter
    -->
    <filter>
        <filter-name>ManageFilter</filter-name>
        <filter-class>com.xjs.filter.ManageFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ManageFilter</filter-name>
        <url-pattern>/manage/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>LoginCheckServlet</servlet-name>
        <servlet-class>com.xjs.servlet.LoginCheckServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginCheckServlet</servlet-name>
        <url-pattern>/loginCheckServlet</url-pattern>
    </servlet-mapping>


</web-app>
  • 完成测试, 看看过滤器是否生效

15.5 Filter 过滤器 url-pattern

  1. url-pattern : Filter 的拦截路径,即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
  2. 精确匹配 <url-pattern>/a.jsp</url-pattern> 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截
  3. 目录匹配 <url-pattern>/manage/*</url-pattern>对应的 请求地址 http://ip[域名]:port/工程路径/manage/xx ,即 web 工程 manage 目录下所有资源 会拦截
  4. 后缀名匹配 <url-pattern>*.jsp</url-pattern> 后缀名可变,比如 *.action *.do 等等对应的 请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截
  5. Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在

15.6 Filter 过滤器生命周期

  • Filter 生命周期图解

在这里插入图片描述


15.7 FilterConfig

  • FilterConfig 接口图

在这里插入图片描述

  • FilterConfig 说明
  1. FilterConfig 是 Filter 过滤器的配置类
  2. Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息
  3. FilterConfig 对象作用是获取 filter 过滤器的配置内容
  • 应用实例
  1. 创建 XjsFilterConfig.java
package com.xjs.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Enumeration;

/**
 * @Author: 谢家升
 * @Date: 2022/3/16-03-16-20:41
 * @Version: 1.0
 */
public class XjsFilterConfig implements Filter {

    private String ip;//从配置获取的IP

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("XjsFilterConfig init() 被调用~~");
        //通过 filterConfig 获取相关参数
        String filterName = filterConfig.getFilterName();
        ip = filterConfig.getInitParameter("ip");
        ServletContext servletContext = filterConfig.getServletContext();
        //获取到该filter所有的配置参数名
        Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
        //遍历枚举
        while (initParameterNames.hasMoreElements()) {
            System.out.println("名字= " + initParameterNames.nextElement());
        }

        System.out.println("filterName= " + filterName);
        System.out.println("ip= " + ip);
        System.out.println("servletContext= " + servletContext);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        //通过forbidden ip 来进行控制
        //先获取访问的IP
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String remoteAddr = httpServletRequest.getRemoteAddr();
        if (remoteAddr.contains(ip)) {
            //封杀
            System.out.println("封杀该网段...");
            httpServletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
            return;//直接返回
        } else {
            //继续访问目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }

    }

    @Override
    public void destroy() {

    }
}

  1. 配置 web.xml
<filter>
    <filter-name>XjsFilterConfig</filter-name>
    <filter-class>com.xjs.filter.XjsFilterConfig</filter-class>
    <!--这里就是给该filter配置的参数,由程序员根据业务逻辑来设置-->
    <init-param>
        <param-name>ip</param-name>
        <param-value>127.0</param-value>
    </init-param>
    <init-param>
        <param-name>port</param-name>
        <param-value>9999</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>XjsFilterConfig</filter-name>
    <url-pattern>/abc/*</url-pattern>
</filter-mapping>

  • 完成测试,直接启动就看到相应的输出信息
  • 简单应用,如果访问 ip 是 128.12 网段 开始的就是返回登录页面

15.8 FilterChain 过滤器链

15.8.1 一句话说明

  • FilterChain: 在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链

15.8.2 基本原理示意图


在这里插入图片描述


15.8.3 应用实例

  1. 需求: 演示过滤器链的使用

在这里插入图片描述


  1. 创建 AFilter.java
package com.xjs.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/3/22-03-22-18:45
 * @Version: 1.0
 */
public class AFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("AFilter doFilter() 的前置代码...");
        System.out.println("AFilter filterChain.doFilter() 被调用~~");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("AFilter doFilter() 的后置代码...");
    }

    @Override
    public void destroy() {

    }
}

  1. 创建 BFilter.java
package com.xjs.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/3/22-03-22-18:47
 * @Version: 1.0
 */
public class BFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("BFilter doFilter() 的前置代码...");
        System.out.println("BFilter filterChain.doFilter() 被调用~~");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("BFilter doFilter() 的后置代码...");
    }

    @Override
    public void destroy() {

    }
}

  1. 创建/使用 /admin/hi.jsp
<%--
  Created by IntelliJ IDEA.
  User: 谢家升
  Date: 2022/3/22
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>hi</title>
</head>
<body>
<h1>admin目录下的 hi.jsp</h1>
<h1>后台管理</h1>
<a href="#">用户列表</a>||<a href="#">添加用户</a>||<a href="#">删除用户</a>
<hr/>
</body>
</html>

  1. 修改 web.xml,配置过滤器

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.xjs.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>BFilter</filter-name>
        <filter-class>com.xjs.filter.BFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

  1. 完成测试

在这里插入图片描述

15.8.4 FilterChain 注意事项和细节

  1. 多个 filter 和目标资源在一次 http 请求,在同一个线程中
  2. 当一个请求 url 和 filter 的 url-pattern 匹配时,才会被执行,如果有多个匹配上,就会 顺序执行,形成一个 filter 调用链(底层可以使用一个数据结构搞定)
  3. 多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象
  4. 多个 filter 执行顺序,和 web.xml 配置顺序保持一致
  5. chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法,如果后面没有过滤器,则执行目标资源
  6. 小结:注意执行过滤器链时,顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter() -> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 ->返回给浏览器页面/数据

15.9 Filter 作业练习

15.9.1 作业布置

  • 需求分析: 使用过滤器,完成如下要求
  1. 点击发表评论页面 topic.jsp, 可以在 showTopic.jsp 显示评论内容
  2. 如果发表的评论内容,有关键字比如 “苹果” “香蕉”, 就返回 topic.jsp, 并提示有禁用词
  3. 要求发表评论到 showTopic.jsp 时,经过过滤器的处理
  4. 禁用词, 配置在过滤器, 在启动项目时动态的获取, 注意处理中文

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


  • 思路分析(程序框架图)
    在这里插入图片描述

1、创建 topic.jsp

<%--
  Created by IntelliJ IDEA.
  User: 谢家升
  Date: 2022/3/17
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>topic</title>
</head>
<body>
<h1>发表对阿凡达电影评论</h1>
敏感词:苹果,香蕉  ${tips}
<br/><br/>
<form action="<%=request.getContextPath()%>/homework/showTopic.jsp" method="post">
    用户:<input type="text" name="username"><br/>
    评论:<textarea name="topic" rows="5" cols="20"></textarea><br/><br/>
    <input type="submit" value="发表评论">

</form>

</body>
</html>

2、创建 TopicFilter.java

package com.xjs.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @Author: 谢家升
 * @Date: 2022/3/17-03-17-7:26
 * @Version: 1.0
 */
public class TopicFilter implements Filter {

    private String[] sensitive;


    public void init(FilterConfig config) throws ServletException {
        //先获取到配置的敏感词
        sensitive = config.getInitParameter("sensitive").split(",");
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("TopicFilter doFilter 被调用~~");
        //解决中文乱码
        req.setCharacterEncoding("utf-8");

        //获取表单数据
        String username = req.getParameter("username");
        String topic = req.getParameter("topic");
        //遍历敏感词
        for (String s : sensitive) {
            if (topic.contains(s)) {
                //你的评论中有敏感词
                req.setAttribute("tips", "你的评论中有敏感词~~");
                req.getRequestDispatcher("/topic.jsp").forward(req, resp);
            }
        }

        //没有敏感词,就放行
        chain.doFilter(req, resp);


    }


    public void destroy() {
    }

}

3、配置过滤器


    <filter>
        <filter-name>TopicFilter</filter-name>
        <filter-class>com.xjs.filter.TopicFilter</filter-class>
        <!--配置敏感词-->
        <init-param>
            <param-name>sensitive</param-name>
            <param-value>苹果,香蕉</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>TopicFilter</filter-name>
        <url-pattern>/homework/*</url-pattern>
    </filter-mapping>

4、创建 showTopic.jsp

<%--
  Created by IntelliJ IDEA.
  User: 谢家升
  Date: 2022/3/17
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>showTopic</title>
</head>
<body>
<h1>你发表的评论是</h1>
评论内容:<%=request.getParameter("topic")%>
</body>
</html>

5、完成测试

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要学就学灰太狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值