06【Filter】

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 06【Filter】,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

06【Filter】

一、过滤器简介

1.1 Filter概述

  • Filter:也称之为过滤器,Filter可以在请求执行WEB资源之前或之后执行,他是一段可重用的代码,这就意味着一个Filter可以管理多个web资源;WEB开发人员通过Filter技术对WEB资源进行一些管理,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

过滤器可以作用于动态或静态内容

  • Filter的执行流程:

在这里插入图片描述

从上图可以看出,在过滤器是在请求到达真实WEB资源之前执行的,因此我们可以在过滤器中做一些统一处理的操作,或者过滤掉一些请求;

1.2 Filter的使用

1.2.1 Filter快速体验

  • 定义一个Servlet:
package com.dfbz.servlet;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo01Servlet....");
    }
}
  • 定义Filter:
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter("/*")            // Filter的拦截规则
public class Demo01Filter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    /**
     * 当有新的请求被Filter拦截到时执行该方法
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter执行了...before");

        // 放行该请求
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("filter执行了...after");
    }

    @Override
    public void destroy() {
    }
}

访问:http://localhost:8080/demo01

查看IDEA控制台:

在这里插入图片描述

需要注意的是,只要访问的路径符合Filter的拦截规则,即使访问的web资源不存在,该请求也会经过Filter

访问:http://localhost:8080/abc

在这里插入图片描述

1.2.2 XML配置Filter

注释掉@WebFilter注解,在web.xml中编写配置:

<filter>
    <filter-name>demo01Filter</filter-name>
    <filter-class>com.dfbz.filter.Demo01Filter</filter-class>
</filter>
<filter-mapping>
    <filter-name>demo01Filter</filter-name>
    <!--所有的请求都经过该Filter-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

1.2.3 Filter的拦截规则

方式 示例
精确匹配 /user/user.jsp
目录匹配 /user/*/emp/*
后缀匹配 *action*.html*.jsp
  • 练习:
浏览器的访问地址 过滤器的配置 是否起作用
http://localhost:8080/aaa /*
http://localhost:8080/aaa /aaa
http://localhost:8080/aaa.do *.do
http://localhost:8080/aaa/bbb /aaa/*
http://localhost:8080/bbb.do /*.do 不会,配置错误
http://localhost:8080/aaa/bbb.action /aaa/*.action 不会,配置错误

Tips:目录匹配不可用和后缀匹配一起使用;

  • 编写Filter测试拦截规则:
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
//@WebFilter("/user")
//@WebFilter("/user.jsp")
//@WebFilter("/user/*")
@WebFilter("*.action")
public class Demo02Filter 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("Demo02Filter...before");

        // 放行该请求
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("Demo02Filter...after");
    }

    @Override
    public void destroy() {
    }
}

1.3 Filter的生命周期

1.3.1 Filter生命周期介绍

  • Filter生命周期:
    • 何时创建?
      • 服务器启动时创建
    • 何时销毁?
      • 服务器关闭时
    • 创建几次?
      • 在整个服务器的运行期间只会创建一次Filter的实例

1.3.2 Filter生命周期相关方法

Filter接口中的方法 作用和执行次数
void init() 初始化的方法,在服务器启动就加载,执行1次
void doFilter() 拦截到请求之后执行的核心逻辑方法,执行N次
void destroy() 销毁,在服务器关闭的时候,执行1次
  • 编写一个Filter,启动服务器观察控制台输出:
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter("/*")
public class Demo03Filter implements Filter {
    /**
     * 当Filter初始化时执行该方法
     *
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter初始化了");
    }

    /**
     * 当有新的请求被Filter拦截到时执行该方法
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filter执行了...before");

        // 放行该请求
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("filter执行了...after");
    }

    /**
     * 当Filter销毁时执行该方法
     */
    @Override
    public void destroy() {
        System.out.println("filter销毁了");
    }
}

访问一个web资源,观察日志;

1.3.3 FilterConfig类

FilterConfig是Filter的配置类,主要用于获取ServletContext对象,以及获取一些其他的filter信息;

  • FilterConfig相关方法:
    • String getFilterName():获取该Filter的名称,默认为该Filter类的全路径名
    • ServletContext getServletContext():获取上下文对象(ServletContext)
    • String getInitParameter(String var1):获取Filter的初始化参数
    • Enumeration<String> getInitParameterNames():获取全部Filter的初始化参数;

  • 测试类:
package com.dfbz.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter(
        filterName = "demo04Filter",            // filter的名称(默认为当前Filter类的全路径名)
        value = "/*",
        initParams = {
                // 自定义一些参数
                @WebInitParam(name = "param1", value = "123"),
                @WebInitParam(name = "param2", value = "456"),
        }
)
public class Demo04Filter implements Filter {

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

        // 获取该Filter的名称
        String filterName = filterConfig.getFilterName();
        System.out.println("【filterName】: " + filterName);

        // 获取初始化的一些自定义参数
        String param1 = filterConfig.getInitParameter("param1");
        String param2 = filterConfig.getInitParameter("param2");

        System.out.println("【param1】: " + param1);
        System.out.println("【param2】: " + param2);

        // 获取上下文对象
        ServletContext app = filterConfig.getServletContext();
        System.out.println(app);
    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Demo04Filter...before");

        // 放行该请求
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("Demo04Filter...after");
    }

    @Override
    public void destroy() {
    }
}

启动服务器,查看IDEA控制台的信息:

在这里插入图片描述

1.4 Filter的拦截方式

拦截方式用于指定过滤器在什么样的请求方式下进行拦截,默认是只对直接来至浏览器发送的请求才拦截。

Tips:默认情况下,只拦截浏览器发送过来的请求

1.4.1 REQUEST

  • REQUEST:代表只拦截浏览器发送过来的请求,不会拦截RequestDispatcherincludeforward发送过来的请求;也是filter的默认值;

案例:编写一个filter只拦截/demo01请求,编写一个Demo02Servlet,跳转到/demo01请求,观察是否会被filter拦截到

  • Demo02Servlet:
package com.dfbz.servlet;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("请求到达了demo02");

        request.getRequestDispatcher("/demo01").forward(request,response);
    }
}
  • Demo05Filter:
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter(value = "/demo01")
public class Demo05Filter 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("Demo05Filter...before");

        // 放行该请求
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("Demo05Filter...after");
    }

    @Override
    public void destroy() {
    }
}

访问:http://localhost:8080/demo01

在这里插入图片描述

访问:http://localhost:8080/demo02

在这里插入图片描述

1.4.2 FORWARD

  • FORWARD只拦截RequestDispatcher的forward()方法发送过来的请求

  • 注解方式配置:
@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.FORWARD})
  • xml方式配置:
<filter>
    <filter-name>demo05</filter-name>
    <filter-class>com.dfbz.filter.Demo05Filter</filter-class>
</filter>

<filter-mapping>
    <filter-name>demo05</filter-name>
    <url-pattern>/demo01</url-pattern>
    <!--拦截类型-->
    <dispatcher>FORWARD</dispatcher>-->
</filter-mapping>

重启服务器,访问:http://localhost:8080/demo01:

在这里插入图片描述

访问:http://localhost:8080/demo02:

在这里插入图片描述

1.4.3 INCLUDE

  • INCLUDE只拦截RequestDispatcher的include()方法发送过来的请求

修改Demo02Servlet:

request.getRequestDispatcher("/demo01").include(request,response);

重启服务器,访问:http://localhost:8080/demo02

在这里插入图片描述

发现Demo05Filter并不会拦截include发送过来的请求(此时Filter的拦截方式为FORWARD)

修改Demo03Filter:

@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.INCLUDE})

重启服务器,访问:http://localhost:8080/demo02

在这里插入图片描述

1.4.4 ERROR

  • ERROR只拦截由于异常或错误引发跳转的请求

在web.xml中配置:

<error-page>
    <!--根据错误码-->
    <!--        <error-code>404</error-code>-->
    <!--根据出现异常的类型-->
    <exception-type>java.lang.ArithmeticException</exception-type>

    <!--如果出现异常就跳转到/demo01-->
    <location>/demo01</location>
</error-page>
  • 修改Demo02Servlet:
package com.dfbz.servlet;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("请求到达了demo02");

//        request.getRequestDispatcher("/demo01").forward(request,response);
//        request.getRequestDispatcher("/demo01").include(request,response);
        
        // 模拟出现异常
        int i = 1 / 0;
    }
}

重启服务器,访问:http://localhost:8080/demo02,出现错误,跳转到/demo01,发现并没有经过Demo05Filter(此时拦截方式是INCLOUD);

  • 修改Demo05Filter:
@WebFilter(value = "/demo01", dispatcherTypes = {DispatcherType.ERROR})

重启服务器,访问:http://localhost:8080/demo02,查看控制台;发现经过了拦截器

1.4.5 ASYNC

在Servlet3.0中,支持了异步的Servlet;默认情况下Filter是不能拦截异步的Servlet的,需要开启ASYNC支持;

  • ASYNC开启拦截异步Servlet的支持

  • 编写异步的Demo03Servlet:
package com.dfbz.servlet;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet(value = "/demo03", asyncSupported = true)            // 1. 开启异步支持
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 2. 开启异步模式
        AsyncContext startAsync = req.startAsync();

        System.out.println("主线程开始: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");

        // 3. 执行任务
        startAsync.start(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务线程1: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");

                // 执行任务
                task();

                // 异步处理完毕
                startAsync.complete();

                ServletResponse response = startAsync.getResponse();
                try {
                    // 响应客户端
                    response.getWriter().write("ok");
                } catch (IOException exception) {
                    exception.printStackTrace();
                }
                System.out.println("任务线程3: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
            }
        });
        System.out.println("主线程结束: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
    }

    public void task() {

        System.out.println("任务线程2: " + Thread.currentThread().getName() + "【" + System.currentTimeMillis() + "】");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 修改Demo05Filter,让其拦截/demo03请求:
@WebFilter(value = "/demo03",dispatcherTypes = {DispatcherType.ASYNC})
public class Demo05Filter implements Filter 

访问:http://localhost:8080/demo03

在这里插入图片描述

默认情况下,Filter不支持拦截异步的Servlet;

修改Demo05Filter:

@WebFilter(value = "/demo03",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.ASYNC},asyncSupported = true)

重启服务器,访问:http://localhost:8080/demo03

在这里插入图片描述

1.5 过滤器的拦截类型小结

过滤类型 作用
REQUEST 只拦截来自浏览器发送过来的请求,默认值
FORWARD 只拦截通过RequestDispatcher.forward()发送的请求
INCLUDE 只拦截通过RequestDispatcher.include()发送的请求
ERROR 只拦截由于异常/错误触发的请求
ASYNC 拦截异步的Servlet,该Filter也要开启异步支持

1.6 Filter小案例

需求:编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。

  1. 有2个Servlet,一个是LoginServlet登录,一个是RegisterServlet注册

  2. 有2个JSP页面,1个是login.jsp,有表单,登录名。1个register.jsp,有表单,有注册的名字。都使用POST提交用户名使用汉字提交。

  3. 使用过滤器,对所有的Servlet的POST方法进行过滤。

  4. 在没有使用过滤器之前,每个Servlet必须加上汉字编码:request.setCharacterEncoding(字符集);字符集与网页的编码要一致

在这里插入图片描述


  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="/login" method="post">
  登录名:<input type="text" name="user"><br>
  <hr>
  <input type="submit" value="登录">
</form>
</body>
</html>
  • register.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="/register" method="post">

    注册名:<input type="text" name="name"><br>
    <hr>
    <input type="submit" value="注册">
</form>
</body>
</html>
  • LoginServlet.java
package com.dfbz.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter out = response.getWriter();
        //得到登录的用户名,并且打印输出
        String user = request.getParameter("user");
        out.print("登录成功,用户名是:" + user);
    }
}
  • RegisterServlet.java
package com.dfbz.servlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter out = response.getWriter();
        //得到注册的用户名
        String name = request.getParameter("name");
        out.print("注册成功,用户名:" + name);
    }
}
  • EncodeFilter.java
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter("/*")
public class EncodingFilter implements Filter {
    public void destroy() {

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //对汉字进行编码
        request.setCharacterEncoding("utf-8");

        //放行,将请求向后传递
        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {

    }
}

二、过滤器链

2.1 过滤器链概念

概念:如果一个用户请求到达web资源中间经过多个过滤器,每个过滤器完成一个具体的功能。这多个过滤器就组成了一个过滤链。

  • 过滤器链执行流程:

在这里插入图片描述

多个过滤器链的执行流程为:过滤器1--->dofilter--->过滤器2---->dofilter---->web资源---->过滤器2--->过滤器1--->客户端

2.2 过滤器链案例

需求:创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。

  • 第一个过滤器:OneFilter.java
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter("/resource")
public class OneFilter implements Filter {
    public void destroy() {

    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("过滤器1:before");
        chain.doFilter(req, resp);
        System.out.println("过滤器1:after");
    }

    public void init(FilterConfig config) throws ServletException {

    }
}
  • 第二个过滤器:TwoFilter.java
package com.dfbz.filter;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebFilter("/resource")
public class TwoFilter implements Filter {

    public void destroy() {

    }
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {

        System.out.println("过滤器2:before");
        chain.doFilter(req, resp); //放行
        System.out.println("过滤器2:after");
    }

    public void init(FilterConfig config) throws ServletException {

    }
}
  • Web资源ResourceServlet.java
package com.dfbz.servlet;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
@WebServlet("/resource")
public class ResourceServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Resource...");
        response.getWriter().write("ok");
    }
}

访问:http://localhost:8080/resource

在这里插入图片描述

2.3 过滤器链的执行顺序

2.3.1 注解方式

使用注解方式配置:根据过滤器的类名字母的先后顺序再执行,例如A>B>C>D…

我们把TowFilter的名字改为AowFilter,重启服务器,访问:http://localhost:8080/resource

在这里插入图片描述

2.3.2 xml配置方式

使用xml方式配置时:配置在前面的filter先执行

<filter>
    <filter-name>one</filter-name>
    <filter-class>com.dfbz.filter.OneFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>one</filter-name>
    <url-pattern>/resource</url-pattern>
</filter-mapping>

<filter>
    <filter-name>two</filter-name>
    <filter-class>com.dfbz.filter.TwoFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>two</filter-name>
    <url-pattern>/resource</url-pattern>
</filter-mapping>

重启服务器,访问:http://localhost:8080/resource

三、过滤器综合案例

3.1 完成案例功能

3.1.1 搭建项目

1)执行SQL语句:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章标题',
  `content` varchar(2550) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文章内容',
  `publish_date` datetime(0) NULL DEFAULT NULL COMMENT '发布时间',
  `publish_user` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发布人',
  `u_id` int(11) NULL DEFAULT NULL COMMENT '发布人id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of article
-- ----------------------------
INSERT INTO `article` VALUES (1, '宝贵的青春', '世界上美丽的东西千千万万,却没有一样比年轻更为美丽;世界上珍贵的东西数也数不清,却没有一样比青春更为宝贵。我们是多么值得骄傲多么让人羡慕啊!而我们若只是挥霍光阴,只是享受,不去奋斗拼搏,那我们真的算拥有青春吗答案是否定的,我们仅有发愤图强,努力耕耘才能做到无愧于青春,无愧于人生,才能拥有一个充实而完美的青春。', '2021-01-10 11:36:26', '张三', 1);
INSERT INTO `article` VALUES (2, '这世界,需要梦想', '这世界,不是所有的地方都能被阳光照耀,也不是时时刻刻都是阳光灿烂。如果你的眼,只是盯着阳光照射不到的地方,或者只是看到眼前的绵绵阴云,那么你的心,就会浸泡在没有阳光的日子里,感受到的,除了负面的情绪,还有什么呢?其实,你完全不必纠结于眼前的景象,你可以放飞自己的那颗自由的心,翱翔在阴云之上。在那时,你的世界就会充满阳光,你的灵魂就会跟着光明飞翔。因而,这世界,需要梦想!', '2020-12-19 11:36:46', '李四', 2);
INSERT INTO `article` VALUES (3, '向着太阳行走', '不改初心,方得始终。这个初心就是对日出的希望。“早晨是岁月的初心,初心在,岁月怎么会老?我每次早起,都是对自己初心的一次回归,不忘初心的人,又怎么会老呢?以朝霞为伴,跟旭日行走,每天都是初生。”这种对日出的初心随时可见,“每一个天亮,我都会重温一次初心,擦拭一下远年的纯真。”每一个天亮,都能感受到作者的初心,真美!', '2020-11-28 11:37:47', '王五', 3);
INSERT INTO `article` VALUES (4, '没尝过努力奋斗的滋味,怎能体会人生的珍贵', '人生的精彩之处,不仅在于取得怎样的成就,更在于拼搏奋斗的过程。\r\n\r\n						奋斗的沿途,也许有痛苦、有失望,但我们终将迎来希望。奋斗的路上,我们会遇见志同道合的朋友,收获更好的自己。我们的人生,也会因为充实的生活而充满色彩。\r\n\r\n						青春不是用来挥霍的,而是要去奋斗的。这是个高速发展的时代,也是奋斗者最好的时代,我们需要随时保持自我学习的能力,朝着前方努力奔跑,越过山丘和沟壑,收获更为精彩的人生。\r\n\r\n						奋斗的滋味,是苦中有甜,甜中带乐。只有品尝过奋斗的滋味,我们才能体会人生的珍贵。趁年轻,去放手一搏吧,这是献给青春最好的礼物,也是对生命最好的馈赠。', '2020-12-30 11:39:30', '张三', 1);
INSERT INTO `article` VALUES (5, '《满江红》· 岳飞', '怒发冲冠,凭栏处、潇潇雨歇。抬望眼、仰天长啸,壮怀激烈。三十功名尘与土,八千里路云和月。莫等闲、白了少年头,空悲切。\r\n靖康耻,犹未雪。臣子恨,何时灭。驾长车,踏破贺兰山缺。壮志饥餐胡虏肉,笑谈渴饮匈奴血。待从头、收拾旧山河,朝天阙。', '2021-01-24 12:18:33', '张三', 1);
INSERT INTO `article` VALUES (6, '《破阵子·为陈同甫赋壮词以寄之》 · 辛弃疾', '醉里挑灯看剑,梦回吹角连营。八百里分麾下炙,五十弦翻塞外声,沙场秋点兵。\r\n马作的卢飞快,弓如霹雳弦惊。了却君王天下事,赢得生前身后名。可怜白发生!', '2021-01-06 12:18:41', '张三', 1);
INSERT INTO `article` VALUES (7, '《过零丁洋》 · 文天祥', '辛苦遭逢起一经,干戈寥落四周星。\r\n山河破碎风飘絮,身世浮沉雨打萍。\r\n惶恐滩头说惶恐,零丁洋里叹零丁。\r\n人生自古谁无死?留取丹心照汗青。', '2021-01-05 12:18:44', '张三', 1);
INSERT INTO `article` VALUES (8, '《滕王阁序》 · 王勃', '豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。 时维九月,序属三秋。潦水尽而寒潭清,烟光凝而暮山紫。俨骖騑于上路,访风景于崇阿;临帝子之长洲,得天人之旧馆。层峦耸翠,上出重霄;飞阁流丹,下临无地。鹤汀凫渚,穷岛屿之萦回;桂殿兰宫,即冈峦之体势。 披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩。闾阎扑地,钟鸣鼎食之家;舸舰弥津,青雀黄龙之舳。云销雨霁,彩彻区明。落霞与孤鹜齐飞,秋水共长天一色。渔舟唱晚,响穷彭蠡之滨;雁阵惊寒,声断衡阳之浦。 遥襟甫畅,逸兴遄飞。爽籁发而清风生,纤歌凝而白云遏。睢园绿竹,气凌彭泽之樽;邺水朱华,光照临川之笔。四美具,二难并。穷睇眄于中天,极娱游于暇日。天高地迥,觉宇宙之无穷;兴尽悲来,识盈虚之有数。望长安于日下,目吴会于云间。地势极而南溟深,天柱高而北辰远。关山难越,谁悲失路之人?萍水相逢,尽是他乡之客。怀帝阍而不见,奉宣室以何年? 嗟乎!时运不齐,命途多舛。冯唐易老,李广难封。屈贾谊于长沙,非无圣主;窜梁鸿于海曲,岂乏明时?所赖君子见机,达人知命。老当益壮,宁移白首之心?穷且益坚,不坠青云之志。酌贪泉而觉爽,处涸辙以犹欢。北海虽赊,扶摇可接;东隅已逝,桑榆非晚。孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭! 勃,三尺微命,一介书生。无路请缨,等终军之弱冠;有怀投笔,慕宗悫之长风。舍簪笏于百龄,奉晨昏于万里。非谢家之宝树,接孟氏之芳邻。他日趋庭,叨陪鲤对;今兹捧袂,喜托龙门。杨意不逢,抚凌云而自惜;钟期既遇,奏流水以何惭? 呜乎!胜地不常,盛筵难再;兰亭已矣,梓泽丘墟。临别赠言,幸承恩于伟饯;登高作赋,是所望于群公。敢竭鄙怀,恭疏短引;一言均赋,四韵俱成。请洒潘江,各倾陆海云尔: 滕王高阁临江渚,佩玉鸣鸾罢歌舞。 画栋朝飞南浦云,珠帘暮卷西山雨。 闲云潭影日悠悠,物换星移几度秋。 阁中帝子今何在?槛外长江空自流。', '2021-01-23 12:18:48', '李四', 2);
INSERT INTO `article` VALUES (9, '《出师表》 · 诸葛亮', '先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。\r\n\r\n宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。\r\n\r\n侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。\r\n\r\n将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。\r\n\r\n亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。\r\n\r\n臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。\r\n\r\n先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。\r\n\r\n愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。\r\n\r\n今当远离,临表涕零,不知所言。', '2021-01-18 12:18:51', '李四', 2);


-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zhangsan', '123', '张三', 20);
INSERT INTO `user` VALUES (2, 'lisi', 'admin', '李四', 19);
INSERT INTO `user` VALUES (3, 'wangwu', '110', '王五', 24);

2)拷贝静态资源

拷贝资源到项目中,将html文件改为jsp文件,在文件首部加入如下代码:

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

在这里插入图片描述

3)拷贝jar包

/WEB-INF中建立lib文件夹,拷贝【配套资料】中准备的jar包

在这里插入图片描述

4)jdbc.properties:

jdbc.username=root
jdbc.password=admin
jdbc.url=jdbc:mysql://localhost:3306/xb
jdbc.driverClassName=com.mysql.jdbc.Driver

5)DataSourceUtils:

package com.dfbz.utils;

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 数据源的工具类
 */
public class DataSourceUtils {

    private static DataSource ds;

    /**
     * 在静态代码块中创建数据源对象
     */
    static {
        // 创建druid数据源
        DruidDataSource dataSource = new DruidDataSource();

        Properties prop = new Properties();
        try {
            // 加载配置文件
            prop.load(DataSourceUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        dataSource.setUsername(prop.getProperty("jdbc.username"));
        dataSource.setPassword(prop.getProperty("jdbc.password"));
        dataSource.setUrl(prop.getProperty("jdbc.url"));
        dataSource.setDriverClassName(prop.getProperty("jdbc.driverClassName"));

        ds=dataSource;
    }

    /**
     * 得到数据源
     */
    public static DataSource getDataSource() {
        return ds;
    }


    /**
     * 从连接池中得到连接对象
     */
    public static Connection getConnection() {
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    /**
     * 释放资源
     */
    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        //关闭结果集
        if (rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //关闭语句对象
        if (stmt!=null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //关闭连接对象
        if (conn!=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭连接
     */
    public static void close(Connection conn, Statement stmt) {
        close(conn, stmt, null);
    }
}

6)实体类:

  • User:
/**
 * user实体类
 */
public class User {

    private Integer id;
    private String username;
    private String password;
    private String name;
    private Integer age;
}
  • Article:
/**
 * article实体类
 */
public class Article {
    private Integer id;
    private String title;
    private String content;
    private Date publishDate;
    private String publishUser;
    private Integer uId;
}

3.1.2 登录业务

1)修改前端页面

<form action="/login" method="post">

2)LoginServlet

package com.dfbz.controller;

import com.dfbz.entity.User;
import com.dfbz.service.UserService;

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

/**
 * 控制器层
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    // 专门用于处理user的业务
    private UserService userService = new UserService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 解决POST提交乱码问题
        request.setCharacterEncoding("UTF-8");

        // 1. 接受前端传递的参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 2.调用service进行业务处理
        User user = userService.findByUsername(username);

        if (user == null) {
            // 说明用户名不存在
            request.setAttribute("error","用户名不存在!");
            request.getRequestDispatcher("/login.jsp").forward(request,response);
            return;
        }

        if (!password.equals(user.getPassword())) {
            // 说明密码错误
            request.setAttribute("error","密码错误!");
            request.getRequestDispatcher("/login.jsp").forward(request,response);
            return;
        }

        // 说明用户名和密码都正确
        HttpSession session = request.getSession();

        // 存入到会话域中,方便其他地方取出当前登录用户的信息
        session.setAttribute("loginUser",user);

        // 重定向到ListServlet进行数据的查询,由ListServlet查询完数据后跳转到list.jsp页面
        response.sendRedirect(request.getContextPath()+"/list");
    }
}

3)UserService

package com.dfbz.service;

import com.dfbz.dao.UserDao;
import com.dfbz.entity.User;

/**
 * 业务逻辑层
 */
public class UserService {

    private UserDao userDao=new UserDao();

    /**
     * 根据用户名查询用户,如果没有查询到返回Null
     * @param username
     * @return
     */
    public User findByUsername(String username) {

        User user=userDao.findByUsername(username);

        return user;
    }
}

4)UserDao

package com.dfbz.dao;

import com.dfbz.entity.User;
import com.dfbz.utils.DataSourceUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 数据层
 */
public class UserDao {

    /**
     * 根据用户名查询用户,如果没有查询到返回Null
     * @param username
     * @return
     */
    public User findByUsername(String username) {

        try {
            // 获取数据库连接
            Connection connection = DataSourceUtils.getConnection();

            // 获取预定义SQL语句对象
            PreparedStatement ps = connection.prepareStatement("select * from user where username=?;");

            // 设置占位符的值
            ps.setString(1,username);

            // 执行查询获取结果集
            ResultSet rs = ps.executeQuery();

            User user=null;

            if(rs.next()){

                Integer id = rs.getInt("id");
                String dbUsername = rs.getString("username");
                String password = rs.getString("password");
                String name = rs.getString("name");
                Integer age = rs.getInt("age");

                user=new User(id,dbUsername,password,name,age);

            }

            return user;

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        return null;
    }
}

3.1.3 文章列表

1)业务分析

业务分析:

1)页面需要取出当前登录用户(从session取出)

2)来到list.jsp页面需要获取当前数据库中的所有文章信息,因此来到这个页面之前就应该准备好

3)文章列表是根据文章发布时间(publish_date)倒叙排序的

在这里插入图片描述

2)ListServlet:

package com.dfbz.controller;

import com.dfbz.entity.Article;
import com.dfbz.service.ArticleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 控制器层
 */
@WebServlet("/list")
public class ListServlet extends HttpServlet {

    private ArticleService articleService=new ArticleService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 查询数据库中所有的文章
        List<Article> articleList=articleService.findAll();

        // 将文章集合添加到域对象
        request.setAttribute("articleList",articleList);

        // 跳转到list.jsp页面
        request.getRequestDispatcher("/list.jsp").forward(request,response);

    }
}

3)ArticleService

package com.dfbz.service;

import com.dfbz.dao.ArticleDao;
import com.dfbz.entity.Article;

import java.util.List;

/**
 * 业务逻辑层
 */
public class ArticleService {
    private ArticleDao articleDao=new ArticleDao();

    /**
     * 查询所有文章,按照发布时间倒叙排序(最晚发布的在最前面)
     * @return
     */
    public List<Article> findAll() {

        List<Article> articleList= articleDao.findAll();

        return articleList;
    }
}

4)ArticleDao

package com.dfbz.dao;

import com.dfbz.entity.Article;
import com.dfbz.entity.User;
import com.dfbz.utils.DataSourceUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 数据层
 */
public class ArticleDao {
    /**
     * 查询所有文章,按照发布时间倒叙排序(最晚发布的在最前面)
     * @return
     */
    public List<Article> findAll() {


        try {
            // 获取数据库连接
            Connection connection = DataSourceUtils.getConnection();

            // 获取预定义SQL语句对象
            PreparedStatement ps = connection.prepareStatement("select * from article order by publish_date desc;");


            // 执行查询获取结果集
            ResultSet rs = ps.executeQuery();

            List<Article> articleList=new ArrayList<>();

            while(rs.next()){

                Integer id = rs.getInt("id");
                String title = rs.getString("title");
                String content = rs.getString("content");
                Date publishDate = rs.getDate("publish_date");
                String publishUser = rs.getString("publish_user");
                Integer uId = rs.getInt("u_id");

                // 添加到articleList集合中
                articleList.add(new Article(id,title,content,publishDate,publishUser,uId));
            }

            return articleList;

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}

5)list.jsp页面

<c:forEach items="${articleList}" var="article">
    <hr>
    <div style="padding:20px;background-color: #eee;border-radius: 10px;">
        <h3 style="text-align: center;">${article.title}</h3>
        <p>${article.content}</p>
        <h4 style="text-align: right;">发布人:${article.publishUser}</h4>
        <h4 style="text-align: left;">发布日期:${article.publishDate}</h4>
    </div>
</c:forEach>

3.1.4 文章添加

1)add.jsp页面

修改表单提交路径和提交方式

<form action="/add" method="post">

2)AddServlet:

package com.dfbz.controller;

import com.dfbz.entity.Article;
import com.dfbz.entity.User;
import com.dfbz.service.ArticleService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;

/**
 * 控制器层
 */
@WebServlet("/add")
public class AddServlet extends HttpServlet {

    // 专门负责处理article的业务逻辑
    private ArticleService articleService=new ArticleService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 设置编码
        request.setCharacterEncoding("UTF-8");

        // 获取前端提交的参数
        String title = request.getParameter("title");
        String content = request.getParameter("content");

        Article article = new Article();
        article.setTitle(title);
        article.setContent(content);

        // 从session中获取用户
        HttpSession session = request.getSession();
        User loginUser = (User) session.getAttribute("loginUser");

        // 发布人
        article.setPublishUser(loginUser.getName());
        // 发布时间
        article.setPublishDate(new Date());
        // 发布人id
        article.setuId(loginUser.getId());

        // 将文章添加到数据库
        articleService.add(article);

        // 重定向到ListServlet重新查询文章数据
        response.sendRedirect(request.getContextPath()+"/list");
    }
}

3)ArticleService

/**
 * 添加文章到数据库
 * @param article
 */
public void add(Article article) {

    articleDao.add(article);
}

4)ArticleDao

/**
 * 添加文章
 * @param article
 */
public void add(Article article) {
    try {
        // 获取数据库连接
        Connection connection = DataSourceUtils.getConnection();

        // 获取预定义SQL语句对象
        PreparedStatement ps = connection.prepareStatement("insert into article values(null,?,?,?,?,?)");

        ps.setString(1,article.getTitle());
        ps.setString(2,article.getContent());
        // 转成时间戳
        ps.setTimestamp(3, new Timestamp(article.getPublishDate().getTime()));
        ps.setString(4,article.getPublishUser());
        ps.setInt(5,article.getuId());

        // 执行SQL语句
        ps.executeUpdate();

    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}

3.2 使用过滤器完成权限校验

在这里插入图片描述

如上图所示,我们来到list.jsp页面时需要显示当前登录人的姓名,换句话说,如果你没有登录那么就不能访问list.jsp页面!

但仅仅是不能访问list.jsp页面就完了吗?肯定不是的,如果我们不让客户端访问list.jsp页面,他一样可以通过访问/list请求来跳转到list.jsp页面,而且用户如果没有登录应该也不能访问add.jsp页面,以及/add请求等,因此我们应该先分析需要拦截什么资源,需要放行什么资源;

3.2.1 分析需求

在这里插入图片描述

通过分析,我们发现静态资源全部需要放行,jsp页面除了login.jsp放行外全部拦截,在请求访问除了/login请求需要放行外,其他请求全部需要登录才可以访问;

需要拦截的请求:/list/list.jsp/add/add.jsp

3.2.3 编写LoginFilter拦截器

  • LoginFilter:
package com.dfbz.filter;

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

// 拦截受限资源
@WebFilter({"/list", "/list.jsp", "/add", "/add.jsp"})
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

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

        // 转成子接口,使功能更加强大
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 获取会话
        HttpSession session = request.getSession();
        // 获取当前登录用户
        Object loginUser = session.getAttribute("loginUser");

        if(loginUser == null){
            // 说明用户没有登录
            response.sendRedirect(request.getContextPath()+"/login.jsp");
            return;
        }

        // 用户登录了: 放行
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

3.3 使用过虑器完成特殊字符过滤

在这里插入图片描述

拦截器除了做请求的拦截与放行外,还可以做一些通用的操作,比如,解决request编码问题!

3.3.1 过滤字符表

DROP TABLE IF EXISTS `words`;
CREATE TABLE `words`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '敏感词汇',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of words
-- ----------------------------
INSERT INTO `words` VALUES (1, '笨蛋');
INSERT INTO `words` VALUES (2, '傻蛋');

3.3.2 字符过滤器

package com.dfbz.filter;

import com.dfbz.utils.DataSourceUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 特殊字符过滤
 * 拦截所有请求
 */
@WebFilter("/*")
public class TextFilter implements Filter {

    private List<String> words = new ArrayList<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 加载敏感词

        try {
            // 获取数据库连接
            Connection connection = DataSourceUtils.getConnection();

            // 获取预定义SQL语句对象
            PreparedStatement ps = connection.prepareStatement("select * from words;");

            // 执行查询获取结果集
            ResultSet rs = ps.executeQuery();

            while (rs.next()) {

                // 添加到词汇集合中
                words.add(rs.getString("word"));
            }


        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");

        // 需要增强request的getParameter()方法

        ServletRequest requestProxy = (ServletRequest) Proxy.newProxyInstance(
                TextFilter.class.getClassLoader(),			// 和目标对象(ServletRequest)一样的类加载器
                request.getClass().getInterfaces(),			// 目标对象(ServletRequest)实现的所以接口的字节码对象
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        // 获取执行的方法名
                        String methodName = method.getName();

                        if ("getParameter".equals(methodName)) {

                            //增强getParameter方法

                            // 原来的功能
                            String returnVal = (String) method.invoke(request, args);

                            for (String word : words) {

                                if (returnVal.contains(word)) {
                                    // 如果包含有敏感词就把敏感词替换为***
                                    returnVal = returnVal.replaceAll(word, "***");
                                }

                            }
                            // 相当于修改了request的getParameter()的返回值
                            return returnVal;
                        }

                        // 其他的方法直接保留原有的功能
                        return method.invoke(request, args);
                    }
                }
        );

        // 放行请求,传递代理对象给具体的servlet
        chain.doFilter(requestProxy, response);
    }

    @Override
    public void destroy() {

    }
}

再次添加文章:

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131639.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!