第一章 Filter过滤器

1.1 Filter的概念

Filter是对客户端访问资源的过滤,符合条件放行,不符合条件不放行。

例如乘火车的安全检查,旅客的身上,行李中均未发现可疑物品,直接放行进站乘车。如果发现有可疑物品,将会被拦截,不得放行进站。安全等级比较高的检查中,会设有多道安全检查岗,旅客必须通过全部的安全检查,才能进站乘车,倘若第一道安全检查被拦截,那就不会到达下一个安全检查岗了。

Filter:一个实现了特殊接口(Filter)的Java类. 实现对请求资源(jsp,servlet,html,)的过滤的功能. 过滤器是一个运行在服务器的程序, 优先于请求资源(Servlet或者jsp,html)之前执行. 过滤器是javaweb技术中最为实用的技术之一。

1.2 Filter的作用

Filter的作用是对目标资源(Servlet,jsp)进行过滤,其应用场景有: 登录权限检查,解决网站乱码,过滤敏感字符等等

1.3 Filter的入门案例

1.3.1 案例目标

实现在请求到达ServletDemo01之前解决请求参数的中文乱码

1.3.2 代码实现

web.xml代码

  1. <servlet>
  2. <servlet-name>servletDemo01</servlet-name>
  3. <servlet-class>com.atguigu.ServletDemo01</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>servletDemo01</servlet-name>
  7. <url-pattern>/ServletDemo01</url-pattern>
  8. </servlet-mapping>

ServletDemo01代码

package com.atguigu.servlet;

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

/**
 * @author Leevi
 * 日期2021-05-18  08:53
 */
public class ServletDemo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        System.out.println("ServletDemo01接收到了一个请求..."+username);
    }
}

前端页面代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <form action="/webday12/demo01" method="post">
        用户名<input type="text" name="username"/><br/>
        <input type="submit"/>
    </form>
</body>
</html>

如果此时没有Filter,那么客户端发送的请求直接到达ServletDemo01,中文请求参数就会发生乱码

1.3.3 创建EncodingFilter

web.xml代码

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.atguigu.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <!--url-pattern表示指定拦截哪些资源-->
    <url-pattern>/demo01</url-pattern>
</filter-mapping>

EncodingFilter代码

package com.atguigu.filter;

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

/**
 * @author Leevi
 * 日期2021-05-18  08:56
 * 编写过滤器的步骤:
 * 1. 写一个类实现Filter接口,并且重写方法
 * 2. 在web.xml中配置该过滤器的拦截路径
 */
public class EncodingFilter implements Filter {
    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //解决请求参数的乱码
        HttpServletRequest request = (HttpServletRequest) req;
        request.setCharacterEncoding("UTF-8");

        //每次有请求被当前filter接收到的时候,就会执行doFilter进行过滤处理
        System.out.println("EncodingFilter接收到了一个请求...");

        //这句代码表示放行
        chain.doFilter(req, resp);
    }

    @Override
    public void init(FilterConfig config) throws ServletException {

    }

}

1.4 Filter的生命周期

1.4.1 回顾Servlet生命周期

Servlet的创建时机

Servlet默认在第一次接收请求的时候创建,我们可以通过<load-on-startup>标签配置Servlet在服务器启动的时候创建

1.4.2 Servlet的销毁时机

Servlet会在服务器关闭或者将项目从服务器上移除的时候销毁

1.4.3 Filter的生命周期和生命周期方法

生命周期阶段 执行时机 生命周期方法
创建对象 Web应用启动时 init方法,通常在该方法中做初始化工作
拦截请求 接收到匹配的请求 doFilter方法,通常在该方法中执行拦截过滤
销毁 Web应用卸载前 destroy方法,通常在该方法中执行资源释放

1.5 过滤器匹配规则

1.5.1 过滤器匹配的目的

过滤器匹配的目的是指定当前过滤器要拦截哪些资源

1.5.2 精确匹配

指定被拦截资源的完整路径:

<!-- 配置Filter要拦截的目标资源 -->
<filter-mapping>
    <!-- 指定这个mapping对应的Filter名称 -->
    <filter-name>FilterDemo01</filter-name>

    <!-- 通过请求地址模式来设置要拦截的资源 -->
    <url-pattern>/demo01</url-pattern>
</filter-mapping>

上述例子表示要拦截映射路径为/demo01的这个资源

1.5.3 目录匹配

相比较精确匹配,使用目录匹配可以让我们创建一个Filter就能够覆盖很多目标资源,不必专门为每一个目标资源都创建Filter,提高开发效率。

在我们配置了url-pattern为/user/*之后,请求地址只要是/user开头的那么就会被匹配。

<filter-mapping>
    <filter-name>Target02Filter</filter-name>

    <!-- 目录匹配:前杠后星 -->
    <!--
        /user/demo01
        /user/demo02
        /user/demo03
        /demo04
    -->
    <url-pattern>/user/*</url-pattern>
</filter-mapping>

1.5.4 扩展名匹配

<filter>
    <filter-name>Target04Filter</filter-name>
    <filter-class>com.atguigu.filter.filter.Target04Filter</filter-class>
</filter>
<filter-mapping>
    <filter-name>Target04Filter</filter-name>
    <url-pattern>*.png</url-pattern>
</filter-mapping>

上述例子表示拦截所有以.png结尾的请求

1.5.5 全局匹配

<filter-mapping>
    <filter-name>Target05Filter</filter-name>

    <!-- 根据Servlet名称匹配 -->
    <servlet-name>/*</servlet-name>
</filter-mapping>

1.6 过滤器链

1.6.1 过滤链的概念

一个请求可能被多个过滤器所过滤,只有当所有过滤器都放行,请求才能到达目标资源,如果有某一个过滤器没有放行,那么请求则无法到达后续过滤器以及目标资源,多个过滤器组成的链路就是过滤器链
img004.png

1.6.2 过滤器链的顺序

过滤器链中每一个Filter执行的顺序是由配置文件的顺序绝对的,准确的说顺序是由web.xml中filter-mapping配置的顺序决定的.

1.6.3 过滤器链案例

web.xml代码

<servlet>
    <servlet-name>servletDemo01</servlet-name>
    <servlet-class>com.atguigu.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo01</servlet-name>
    <url-pattern>/ServletDemo01</url-pattern>
</servlet-mapping>

ServletDemo01代码

public class ServletDemo01 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("ServletDemo01接收到了请求...");
    }
}

1.6.4 创建多个Filter拦截Servlet

<filter-mapping>
    <filter-name>TargetChain03Filter</filter-name>
    <url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>TargetChain02Filter</filter-name>
    <url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>TargetChain01Filter</filter-name>
    <url-pattern>/Target05Servlet</url-pattern>
</filter-mapping>

1.7 过滤器注解开发

@WebFilter注解:注解属性urlPatterns,配置要拦截的资源

使用注解开发时,过滤器的顺序是由过滤器的类名的自然顺序决定

第二章 Listener监听器

2.1 监听器的概念

监听器:专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。
Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext,HttpSession 和HttpServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

2.2 Servlet监听器的分类(了解)

2.2.1 ServletContextListener

作用:监听ServletContext对象的创建与销毁

方法名 作用
contextInitialized(ServletContextEvent sce) ServletContext创建时调用
contextDestroyed(ServletContextEvent sce) ServletContext销毁时调用

ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。

2.2.2 HttpSessionListener

作用:监听HttpSession对象的创建与销毁

方法名 作用
sessionCreated(HttpSessionEvent hse) HttpSession对象创建时调用
sessionDestroyed(HttpSessionEvent hse) HttpSession对象销毁时调用

HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。

2.2.3 ServletRequestListener

作用:监听ServletRequest对象的创建与销毁

方法名 作用
requestInitialized(ServletRequestEvent sre) ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre) ServletRequest对象销毁时调用

ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。

2.2.4 ServletContextAttributeListener

作用:监听ServletContext中属性的添加、移除和修改

方法名 作用
attributeAdded(ServletContextAttributeEvent scab) 向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab) 从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab) 当ServletContext中的属性被修改时调用

ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletContext() 获取ServletContext对象

2.2.5 HttpSessionAttributeListener

作用:监听HttpSession中属性的添加、移除和修改

方法名 作用
attributeAdded(HttpSessionBindingEvent se) 向HttpSession中添加属性时调用
attributeRemoved(HttpSessionBindingEvent se) 从HttpSession中移除属性时调用
attributeReplaced(HttpSessionBindingEvent se) 当HttpSession中的属性被修改时调用

HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getSession() 获取触发事件的HttpSession对象

2.2.6 ServletRequestAttributeListener

作用:监听ServletRequest中属性的添加、移除和修改

方法名 作用
attributeAdded(ServletRequestAttributeEvent srae) 向ServletRequest中添加属性时调用
attributeRemoved(ServletRequestAttributeEvent srae) 从ServletRequest中移除属性时调用
attributeReplaced(ServletRequestAttributeEvent srae) 当ServletRequest中的属性被修改时调用

ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletRequest () 获取触发事件的ServletRequest对象

2.2.7 HttpSessionBindingListener

作用:监听某个对象在Session域中的创建与移除

方法名 作用
valueBound(HttpSessionBindingEvent event) 该类的实例被放到Session域中时调用
valueUnbound(HttpSessionBindingEvent event) 该类的实例从Session中移除时调用

HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取当前事件涉及的属性名
getValue() 获取当前事件涉及的属性值
getSession() 获取触发事件的HttpSession对象

2.2.8 HttpSessionActivationListener

作用:监听某个对象在Session中的序列化与反序列化。

方法名 作用
sessionWillPassivate(HttpSessionEvent se) 该类实例和Session一起钝化到硬盘时调用
sessionDidActivate(HttpSessionEvent se) 该类实例和Session一起活化到内存时调用

HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象。

2.3 ServletContextListener的使用

2.3.1 作用

ServletContextListener是监听ServletContext对象的创建和销毁的,因为ServletContext对象是在服务器启动的时候创建、在服务器关闭的时候销毁,所以ServletContextListener也可以监听服务器的启动和关闭

2.3.2 使用场景

将来学习SpringMVC的时候,会用到一个ContextLoaderListener,这个监听器就实现了ServletContextListener接口,表示对ServletContext对象本身的生命周期进行监控。

package com.atguigu.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 包名:com.atguigu.listener
 *
 * @author Leevi
 * 日期2021-06-19  10:26
 * 编写监听器的步骤:
 * 1. 写一个类实现对应的:Listener的接口(我们这里使用的是ServletContextListener),并且实现它里面的方法
 *    1.1 contextInitialized()这个方法在ServletContext对象被创建出来的时候执行,也就是说在服务器启动的时候执行
 *    1.2 contextDestroyed()这个方法会在ServletContext对象被销毁的时候执行,也就是说在服务器关闭的时候执行
 *
 * 2. 在web.xml中注册(配置)监听器
 */
public class ContextLoaderListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("在服务器启动的时候,模拟创建SpringMVC的核心容器...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("在服务器启动的时候,模拟销毁SpringMVC的核心容器...");
    }
}

2.3.3 注册监听器

<listener>
    <listener-class>com.atguigu.listener.ContextLoaderListener</listener-class>
</listener>

第三章 书城项目第六阶段

功能一 登录检查

3.1 目标

把项目中需要保护的功能保护起来,没有登录不允许访问

  • 购物车功能
  • 订单功能

3.2 思路

img01.png

3.3 代码实现

3.3.1 拦截受保护资源的请求

  1. 修改受保护的Servlet的映射路径
    购物车资源地址:/protected/CartServlet
    订单资源地址(明天学):/protected/OrderServlet
  2. Filter拦截的地址:/protected/*

3.3.2 对访问购物车资源的地址进行修改

所有异步请求访问购物车的路径,都需要在cart前面加上protected/

web.xml中修改CartServlet的url-pattern
<servlet>
    <servlet-name>CartServlet</servlet-name>
    <servlet-class>com.atguigu.servlet.model.CartServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>CartServlet</servlet-name>
    <url-pattern>/protected/cart</url-pattern>
</servlet-mapping>

3.3.3 创建执行登录检查的Filter

@WebFilter(urlPatterns = "/protected/*")
public class LoginFilter implements Filter {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String method = request.getParameter("method");
        User user = (User)request.getSession().getAttribute("user");
        if (user == null){
            if ("toCartPage".equals(method) || "clearCart".equals(method)){
                //同步请求,重定向到登录去
                response.sendRedirect(request.getContextPath()+"/user?method=toLoginPage");
            }
            else {
                response.getWriter().write(objectMapper.writeValueAsString(new CommonResult(false,"","unlogin")));
            }
            return;
        }
        filterChain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

3.3.4 修改所有发送与购物车相关的异步请求的代码

判断响应数据的flag如果为false,并且message为unLogin,那么则使用location.href跳转到登录页面

showCart(){
    //发送异步请求获取购物车的信息
    axios({
        "method":"post",
        "url":"protected/cart",
        "params":{
            "method":"getCartJSON"
        }
    }).then(response => {
        if (response.data.flag){
            this.cart = response.data.resultData
        }else {
            if (response.data.message == "unLogin") {
                location.href = "user?method=toLoginPage"
            }else {
                //说明显示购物车信息失败
                layer.msg("显示购物车信息失败")
            }
        }
    } )
}