【教程】JavaWeb之Filter技术


1.基本概念

在现实生活中,我们涉及到对茶叶的过滤,对污水的过滤,对沙子的过滤…
而在JavaWeb中也涉及到过滤,这时候我们就需要用到Filter,而Filter本意为”过滤“的含义,它的作用是过滤一些内容和数据;
专业术语:它是JavaWeb的三大组件之一,三大组件分别为:Servlet、Filter、Listener 。

2.工作方式

在这里插入图片描述

可以看到,过滤器相当于浏览器与Web资源之间的一道过滤网,在访问资源之前通过一系列的过滤器对请求进行修改、判断以及拦截等,也可以对响应进行修改、判断以及拦截等。

3.使用方式

先看一个登录例子,此示例有一个登录页面(login.jsp),一个主页(main.jsp),要求登录成功后跳转至main.jsp,并显示用户名。
项目结构:
在这里插入图片描述

login.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<form action="login" method="post">
    用户名: <input type="text" name="userName"/><br/>&nbsp;&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

main.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>主页面</title>
</head>
<body>
<h1>登录成功,欢迎${sessionScope.userName}使用!</h1>
</body>
</html>

LoginServlet.java

@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1.接收前端页面输入的用户名和密码信息并打印
        String userName = request.getParameter("userName");
        System.out.println("接收到的用户名为:" + userName);
        String password = request.getParameter("password");
        System.out.println("接收到的密码为:" + password);
        // 2.使用固定的用户名和密码信息来进行登录的校验
        if ("admin".equals(userName) && "123456".equals(password)) {
            System.out.println("登录成功,欢迎使用!");
            // 存储用户名信息
            request.getSession().setAttribute("userName", userName);
            response.sendRedirect("main.jsp");
        } else {
            System.out.println("用户名或密码错误,请重新输入!");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }

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

登陆成功后,确实跳转到了主页,效果如下:
在这里插入图片描述

在这里插入图片描述
但是假如拷贝main.jsp这个路径,同样可以在浏览器另外一个页面进行访问,只是此时没有登录的用户名显示而已。
在这里插入图片描述
而我们现在要做的就是,如果未登录,就不让他访问主页。这个时候就需要用到Filter了。实现如下:

扫描二维码关注公众号,回复: 12578290 查看本文章
  • 自定义类实现Filter接口并重写doFilter方法
public class LoginFilter implements Filter {
    
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        // 1.实现对用户访问主页面的过滤操作,也就是只有用户登录后才能访问主页面,否则一律拦截
        // 判断session中是否已有用户名信息,若没有则进行拦截,否则放行
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
        HttpSession session = httpServletRequest.getSession();
        Object userName = session.getAttribute("userName");
        // 获取Servlet的请求路径
        String servletPath = httpServletRequest.getServletPath();
        // 若没有登录,则回到登录页面
        if (null == userName && !servletPath.contains("login")) {
    
    
            servletRequest.getRequestDispatcher("login.jsp").forward(servletRequest, servletResponse);
        } else {
    
    
            // 若已经登录,则放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {
    
    

    }
}

  • 在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>
        <filter-name>LoginFilter</filter-name>
        <filter-class>com.example.demo01.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>LoginFilter</filter-name>
        <url-pattern>/main.jsp</url-pattern>
    </filter-mapping>
</web-app>

这样,再通过拷贝路径直接访问主页就访问不了,会被拦截,然后跳转到登录页面,保证了主页的安全。
ps:需要注意的是,若需要放行,必须调用方法 chain.doFilter(request,response),此为固定语句。

4.Filter接口

(1)javax.servlet.Filter接口,用于描述过滤器对象。其他类只要实现了Filter接口接口,就可以实现过滤器的功能。
(2)Filter接口主要有3个方法 ,这3个方法代表了Filter的生命周期。三个方法分别是:
在这里插入图片描述

public class LifeFilter implements Filter {
    
    
    public LifeFilter() {
    
    
        System.out.println("构造方法执行!");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
        System.out.println("初始化操作正在火热进行中...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        System.out.println("阻拦一切不合理的访问哦!");
    }

    @Override
    public void destroy() {
    
    
        System.out.println("销毁操作执行完毕了!");
    }
}
<?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>
        <filter-name>LifeFilter</filter-name>
        <filter-class>com.lagou.demo02.LifeFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>LifeFilter</filter-name>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>
</web-app>

运行效果
在这里插入图片描述
在这里插入图片描述
可以看到,构造方法先于init方法执行。
构造方法和 init 方法都是在启动服务器或者部署服务的时候调用;
doFilter 方法在浏览器请求路径的时候调用;
destroy 方法在重新部署服务或者停止服务的时候调用。

5. FilterConfig接口

(1)javax.servlet.FilterConfig接口,用于描述过滤器的配置信息。
(2)FilterConfig接口主要有4个方法 ,这4个方法分别是:
在这里插入图片描述
(3)不知你有没有注意到,在Filter 的 init 方法中参数的引用就是FilterConfig接口,所以可以在 init 方法里面进行FilterConfig 接口相关方法的调用,示例如下:
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>
        <filter-name>LifeFilter</filter-name>
        <filter-class>com.example.demo02.LifeFilter</filter-class>
        <init-param>
            <param-name>userName</param-name>
            <param-value>admin</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123456</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>LifeFilter</filter-name>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>
</web-app>

LifeFilter.java:

public class LifeFilter implements Filter {
    
    
    public LifeFilter() {
    
    
        System.out.println("构造方法执行!");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    
        System.out.println("初始化操作正在火热进行中...");
        System.out.println("获取到的过滤器名称为:" + filterConfig.getFilterName());
        String userName = filterConfig.getInitParameter("userName");
        System.out.println("获取到指定初始化参数的数值为:" + userName);  // admin
        Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
    
    
            // userName password
            System.out.println("获取到的初始化参数名为:" + initParameterNames.nextElement());
        }
        ServletContext servletContext = filterConfig.getServletContext();
        System.out.println("获取到的上下文对象是:" + servletContext);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
    
        System.out.println("阻拦一切不合理的访问哦!");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
    
    
        System.out.println("销毁操作执行完毕了!");
    }
}

运行结果
在这里插入图片描述

6.多个过滤器的使用

在这里插入图片描述
根据上图,
请求的时候,执行顺序是:过滤器1->过滤器2->过滤器3;
返回的时候,执行顺序是:过滤器3->过滤器2->过滤器1。
多个过滤器,可以实现多层过滤,实现方式是创建多个继承Filter接口的java类,并在web.xml中进行配置。
多个过滤器执行的先后顺序和url的映射配置有关系,哪个配置在前面就执行哪个。
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>
        <filter-name>BFilter</filter-name>
        <filter-class>com.example.demo02.BFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>*.avi</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>com.example.demo02.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>*.avi</url-pattern>
    </filter-mapping>
</web-app>

A过滤器:

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("这是第一道防线!");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("第一道防线返回!");
    }

    @Override
    public void destroy() {
    
    

    }
}

B过滤器:

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("这是第二道防线!");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("第二道防线返回!");
    }

    @Override
    public void destroy() {
    
    

    }
}

运行结果:
在这里插入图片描述
可以看到,由于过滤器B的 filter-mapping 配置写在前面,所以请求的时候先执行了过滤器B。

7.过滤器优点

  • 实现代码的“可插拔性”,即增加或减少某个功能模块,不会影响程序的正常执行(如刚才的登录功能去除了过滤器也可以正常登录)。
  • 可以将多个相同处理逻辑的模块集中写在过滤器里面,实现代码的复用(如对每个请求做身份验证)。

猜你喜欢

转载自blog.csdn.net/u012660464/article/details/109457850
今日推荐