欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(Java)过滤器Filter及“登录案例”

程序员文章站 2022-06-18 17:41:55
...

1. 过滤器Filter

1.1 Filter概念

  • 过滤器: 过筛子,符合条件的过去,不符合条件不能过去.

  • 生活比喻: 安检,检查安全的人与物才可以通过放行

  • 程序: 客户端需要访问服务器的目标资源,在客户端和服务器资源之间设置过滤器, 符合要求放行

2. Filter的入门程序

  • 需求:

    • 浏览器要访问HelloServlet
    • 途径过滤期MyFilter, 若MyFilter放行,可执行访问到HelloServlet; 若不放行,无法访问HelloServlet
  • 执行图解:
    (Java)过滤器Filter及“登录案例”

  • 实现步骤:

    1. 编写Servlet, 在web.xml配置Servlet访问路径
    2. 编写Filter,定义类, 实现接口Filter, 实现接口中抽象方法, 在web.xml配置Filter的访问过滤路径

代码: HelloServlet


public class HelloServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //控制台打印
        System.out.println("HelloServlet 执行了");
        //浏览器响应
        response.getWriter().println("Hello Servlet");
    }

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

代码: MyFilter


public class MyFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println("MyFilter 执行了");

        //执行过滤后放行, 进行后续的请求访问
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

代码: 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_3_1.xsd"
         version="3.1">
    <!--配置servlet-->
    <filter>
        <filter-name>my</filter-name>
        <filter-class>com.itheima.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>my</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--配置Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.itheima.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

3. Filter的生命周期

  • 过滤器对象的创建,是Tomcat服务器启动

    • init(FilterConfig config)过滤器对象被创建的时候调用,FilterConfig 对象tomcat引擎创建
  • 过滤器执行过滤的方法,过滤被访问资源的时候,必须是被过滤器过滤器的资源

    • doFilter(request,response)
  • 过滤器对象销毁的方法,销毁之前调用,服务器关闭

    • destroy()
    package cn.itcast.web.filter;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * 1 创建类 实现Filter接口
     * 2 在web.xml中注册
     * 3 在web.xml中映射拦截的路径
     */
    public class Filter1 implements Filter {
        String encoding;
    
        public Filter1() {
            System.out.println("======= 1 执行Filter1的构造方法... ...");
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("======= 2 执行Filter1的初始化 init 方法... ...");
            encoding = filterConfig.getInitParameter("encoding");
            System.out.println("编码集: " + encoding);
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("======= 3 执行Filter1的执行过滤 doFilter 方法... ... 编码集:" + encoding);
            // 因为父类不能调用子类的方法,所以向下转型
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String uri = httpServletRequest.getRequestURI();
            System.out.println("执行过滤器1 ... ... 访问的路径: " + uri);
    
            // 允许访问目标资源 : 放行
            filterChain.doFilter(servletRequest, servletResponse);
    
            //System.out.println("放行之后执行... ...");
        }
    
        @Override
        public void destroy() {
            System.out.println("======= 4 执行Filter1的销毁 destroy 方法... ...");
    
        }
    }
    

4. Filter的url-pattern配置

  • 完全匹配
<!-- 
    过滤资源,只有hello
    绝对匹配 <url-pattern>/hello</url-pattern>
    只能过滤指定的资源
-->
<url-pattern>/hello</url-pattern>
  • 目录匹配
<!--
   目录匹配,过滤器中最常见
   /abc/*  过滤abc目录下的所有资源
   一次过滤一片资源
   过滤后台资源 /admin/*
-->
<url-pattern>/admin/*</url-pattern>
  • 后缀名匹配
<!-- 
  后缀名匹配,一般不使用
  *.jsp  访问所有jsp文件
-->
<url-pattern>*.jsp</url-pattern>

5. 注解配置Filter

@WebFilter(urlPatterns="/过滤资源")

@WebFilter("/*")
public class ChinaFilter implements Filter {
    //代码省略...
}
package cn.itcast.web.filter;

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

// 方式一
//@WebFilter(urlPatterns = {"/hello.jsp", "/helloServlet"})

// 方式二
// @WebFilter(urlPatterns = {"/hello.jsp"})

// 方式三
// @WebFilter(urlPatterns = "/hello.jsp")

// 方式四
@WebFilter("/hello.jsp")
public class Filter3 implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // 因为父类不能调用子类的方法,所以向下转型
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        String uri = httpServletRequest.getRequestURI();
        System.out.println("执行过滤器3 ... ... 访问的路径: " + uri);

        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

6. Filter 处理中文乱码

  • 需求:

​ 使用过滤器Filter, 处理所有请求的中文乱码

  • 代码: ChinaServlet
@WebServlet("/china")
public class ChinaServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        System.out.println("ChinaServlet , 获取请求参数 username = " + username);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 代码: ChinaFilter
@WebFilter("/*")
public class ChinaFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //在过滤器中,设置request对象的编码表
        req.setCharacterEncoding("utf-8");
        //设置response缓冲区的编码表,通知浏览器的解码
        resp.setContentType("text/html;charset=utf-8");

        System.out.println("ChinaFilter执行了, 过滤中文,设置编码utf-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

    public void destroy() {
    }
}

7. 过滤器链 FilterChain的执行过程

Filter中的过滤器链 FilterChain: 由Tomcat引擎创建对象

作用: 维护过滤器执行顺序

(Java)过滤器Filter及“登录案例”

小结: Servlet中doGet方法参数 request, response对象, 由Tomcat引擎创建, 经过多个过滤器一层层传递

8. 多个过滤器的先后执行顺序

  • web.xml配置:

    和配置文件的编写顺序决定运行的顺序,准确的说法是,根据mapping的顺序决定 (由上到下执行)

  • 注解开发

    注解开发没有配置文件

    按照类名的自然顺序决定: A-B-C

    如果存在配置文件,配置文件优先

9. 过滤器案例: 登录验证(权限校验)

  • 问题描述:在登录的时候,若登录成功会跳转到登录成功的页面,该页面有一个网址。若用户没有经过登录直接访问该网址,应该被拦截下来,让用户去登录。
  • 方法分析:
    • 在登录成功网址前加个过滤器,判断是否登录,若登录则可以放行,若没有,则跳转到登录页面并告知用户需要登录
    • 判断是否登录的方法:在登录Servlet中,若登录成功,将用户名放在session域中;在过滤器中从session域中取出用户名,若能取到,就证明已经登录了,放行。

代码

  • 登录页面
1. 访问项目的资源。验证其是否登录
2. 如果登录了,则直接放行。
3. 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。

9.2 实现

  • 登录页面(非常丑,示例一下)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
    <div style="color: red;size: 10px">${message}</div>
    <form action="${pageContext.request.contextPath}/login" method="post">
        用户名:<input type="text" name="username"><br>
        密的码:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

  • 登录成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>欢迎页面</title>
</head>
<body>
    <div style="font-size: 28px">欢迎登录</div>
</body>
</html>

  • LoginServlet
package 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;

@WebServlet(urlPatterns = "/login")
public class LoginServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取表单提取的数据
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 判断是否 登录成功
        if (username.equals("tom") && password.equals("123")){
            //登录从成功,用户名存储在session中
            request.getSession().setAttribute("username",username);

            // 重定向,需要些web应用名称

            response.sendRedirect(request.getContextPath()+"/resource/welcome.jsp");

        }else {
            //登录失败,回到登录页面,告知用户
            request.setAttribute("message","登录失败,用户名或密码错误");
            request.getRequestDispatcher("/login.jsp").forward(request,response);

        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}
  • 过滤器
package filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter(urlPatterns = "/resource/*")
public class loginFilter implements Filter {
    /**
     * 登录的过滤: 不能直接访问登录成功页面,必须登录后才行
     *  所以要判断是否登录,若登录,正常放行
     *  若没有登录直接输入了登录成功页面的网址,那么就跳转到登录页面
     *  那么如何判断是否登录了呢?
     *  从session域中取出username
     *  为什么从session域中取呢,因为在登录Servlet中,登录成功后将用户名存储到了session中
     *  如果为空,就拦截(页面跳转到登录界面)
     *  如果不为空,走正常程序()
     */

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // servletRequest转成HttpServletRequest子接口
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        //servletResponse转成HttpServletResponse
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        // 获取session域中的username
        String username = (String)request.getSession().getAttribute("username");

        if (username == null){
            // 没有登录,退回到登录页面并告知用户
            request.getSession().setAttribute("message","请先登录");
            request.getRequestDispatcher("/login.jsp").forward(request,response);
        }else {
            // 检测到登录,放行
            filterChain.doFilter(request,response);

        }
    }

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

    }

    @Override
    public void destroy() {

    }
}

9.3 验证

  • 没有登录时,访问登录成功页面:
    (Java)过滤器Filter及“登录案例”