JavaWeb之Filter过滤器教程

什么是Filter过滤器?

Filter在Web开发中通常被称为过滤器,是一种用于拦截处理请求和响应的组件。它可以在请求到达目标资源之前进行拦截,也可以对响应进行拦截。通过使用过滤器,可以对请求参数进行校验、对响应内容进行压缩和加密等操作,从而提高系统的安全性、性能和可靠性。

过滤器可以串联在处理请求的整个路径中,对请求进行预处理和后处理。例如,可以过滤掉恶意请求、对请求参数进行统一格式化等。在处理响应时,可以统一设置响应头、对响应内容进行压缩等。

  • Filter 过滤器它是 JavaWeb 的三大组件之一。
  • Filter 过滤器它是 JavaEE 的规范。也就是接口
  • Servlet3.0可以用注解@WebFilter,Servlet2.5可以使用xml文件配置。
  • Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter("*.do")表示拦截所有以.do结尾的请求

拦截请求常见的应用场景有: 1、权限检查 2、日记操作 3、事务管理 ……等等。

假设有一个admin目录,使用用户登录之后才可以访问这个目录下的资源。

思考:
我们知道,用户登录之后都会把用户登录的信息保存到 Session 域中。所以要检查用户是否登录,可以判断 Session 中否包含有用户登录的信息即可!

<body>
  <%
    Object user = session.getAttribute("user");
    // 如果等于 null,说明还没有登录
    if (user == null) {
        request.getRequestDispatcher("/login.jsp").forward(request,response);
        return;
    }
  %>

<h2>这是info.jsp</h2>
</body>

结论:
这种在jsp页面方式虽然能判断用户是否登录限制资源访问,但如果在html页面下无法使用这种判断方式,有了局限性,所以最好是使用Filte过滤器限制访问。

Filter工作流程

Filter工作流程图

Filter案例实操

Filter 过滤器的使用步骤:

1、编写一个类去实现 Filter 接口

2、实现过滤方法 doFilter()

3、到 web.xml 中去配置 Filter 的拦截路径

<body>
    这是登录页面。login.jsp 页面 <br>
    <form action="http://localhost:8080/day05_cookie/login" method="get">
        用户名:<input type="text" name="username"/> <br>
        密 码:<input type="password" name="password"/> <br>
        <input type="submit" />
    </form>
</body>
public class AdminFilter implements Filter {
    public AdminFilter() {
        System.out.println("Filter实现类的构造器初始化操作...");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter过滤器初始化操作...");
    }

    /**
     * doFilter():专门用于拦截请求,过滤响应
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        var httpServletRequest = (HttpServletRequest) servletRequest;
        var session = httpServletRequest.getSession();
        var user = session.getAttribute("user");
        if (user == null) {
            //如果等于null,则没有登录成功
            servletRequest.getRequestDispatcher(File.separator + "login.jsp").forward(servletRequest,servletResponse);
        } else {
            //登录成功,放行当前请求和响应
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {
        System.out.println("Filter过滤器销毁操作...");
    }
}
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        var username = req.getAttribute("username");
        var password= req.getAttribute("password");
        if ("admin".equals(username) && "123456".equals(password)) {
            req.getSession().setAttribute("user",username);
            resp.getWriter().write("登录成功");
        } else {
            req.getRequestDispatcher(File.separatorChar + "login.jsp").forward(req,resp);
        }
    }
}
<!-- 配置Filter过滤器 -->
<filter>
    <!-- Filter实现类的别名 -->
    <filter-name>adminFilter</filter-name>
    <!-- Filter实现类的全类名 -->
    <filter-class>com.evan.java.AdminFilter</filter-class>
</filter>
<!-- 映射当前请求到Filter实现类 -->
<filter-mapping>
    <!-- 当前请求的拦截路径到哪个Filter使用 -->
    <filter-name>adminFilter</filter-name>
    <!-- 配置拦截路径:
            / 表示请求地址是 http://ip:port/工程路径/  映射到当前工程模块的web目录
            /admin/* 表示请求地址是 http:ip:port/工程路径/admin/*
         -->
    <url-pattern>/admin/*</url-pattern>
</filter-mapping>

<!-- 配置servlet访问 -->
<servlet>
    <servlet-name>loginServlet</servlet-name>
    <servlet-class>com.evan.java.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>loginServlet</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

结论:
当login.jsp登录成功时当前请求执行目标资源,Filter检查满足规则则会放行,继续执行下一个操作;当登录失败去执行当前请求的目标资源时会被拦截跳转到login.jsp

Filter生命周期

public class AdminFilter implements Filter {
    public AdminFilter() {
        System.out.println("1.Filter实现类的构造器初始化操作...");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("2.Filter过滤器初始化操作...");
    }

    /**
     * doFilter():专门用于拦截请求,过滤响应
     * @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("3.doFilter执行请求和响应的拦截操作...");
    }

    @Override
    public void destroy() {
        System.out.println("4.Filter过滤器销毁操作...");
    }
}

Filter声明周期的执行过程:

  1. 执行FIlter实现类的构造器
  2. 执行Filter初始化方法
  3. 执行Filter的doFilter拦截方法
  4. 执行FIlter的销毁方法
    步骤1和2是在web工程启动时执行,后续web工程没有关闭,再次执行Filter操作会执行步骤3,步骤3在每次拦截到请求时执行,步骤4在工程停止时执行。

FilterConfig类

FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter对象的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。
FilterConfig 类的作用是获取 filter 过滤器的配置内容 :
1、获取 Filter 的名称 filter-name 的内容

2、获取在 Filter 中配置的 init-param 初始化参数

3、获取 ServletContext 对象

public class AdminFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter过滤器初始化操作...");
        //获取Filter的名称filter-name的内容
        System.out.println("filter-name值:" + filterConfig.getFilterName());
        //获取web.xml中配置的init-param初始化参数
        System.out.println("初始化参数username的值是:" + filterConfig.getInitParameter("username"));
        //获取ServletContext对象
        System.out.println("ServletContext对象:" + filterConfig.getServletContext());
    }
}
<!-- 配置Filter过滤器 -->
<filter>
    <!-- Filter实现类的别名 -->
    <filter-name>adminFilter</filter-name>
    <!-- Filter实现类的全类名 -->
    <filter-class>com.evan.java.AdminFilter</filter-class>
    <!-- 配置Filter初始化参数值 -->
    <init-param>
        <param-name>username</param-name>
        <param-value>admin</param-value>
    </init-param>
</filter>
<!-- 映射当前请求到Filter实现类 -->
<filter-mapping>
    <!-- 当前请求的拦截路径到哪个Filter使用 -->
    <filter-name>adminFilter</filter-name>
    <!-- 配置拦截路径:
            / 表示请求地址是 http://ip:port/工程路径/  映射到当前工程模块的web目录
            /admin/* 表示请求地址是 http:ip:port/工程路径/admin/* 映射到当前工程模块的web目录下的admin目录
         -->
    <url-pattern>/admin/*</url-pattern>
</filter-mapping>

FilterChain 过滤器链

Filter: 过滤器 。
Chain :链,链条。
FilterChain就是过滤器链条。


FilterChain工作流程图
多个FilterChain执行的特点:

1、所有Filter和目标资源默认都执行在同一个线程中。
2、多个Filter共同执行的时候,它们都使用同一个Request对象。

FilterChain.doFilter方法的作用:

1、执行下一个Filter过滤器(如果有Filter)
2、执行目标资源(下一步没有Filter执行目标资源)
在多个Filter过滤器执行的时候,他们执行的优先顺序是由他们在web.xml中配置从上而下的顺序决定。

案例实操
//Filter1实现类
public class Filter1 implements Filter {

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

//Filter2实现类
public class Filter2 implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter2 前置代码");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Filter2 后置代码");
    }
}
<filter>
    <filter-name>filter1</filter-name>
    <filter-class>com.evan.java.Filter1</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter1</filter-name>
    <url-pattern>/filterTest.jsp</url-pattern>
</filter-mapping>

<filter>
    <filter-name>filter2</filter-name>
    <filter-class>com.evan.java.Filter2</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter2</filter-name>
    <url-pattern>/filterTest.jsp</url-pattern>
</filter-mapping>

FilterChain的执行结果:
Filter1 前置代码
Filter1的线程:http-nio-8080-exec-3
Filter2 前置代码
Filter2的线程:http-nio-8080-exec-3
filterText.jsp
Filter2 后置代码
Filter1 后置代码

FIilterChain的执行顺序:
先执行Filter链的所有前置代码以及doFIlter的拦截方法,然后执行Filter链的所有后置代码;同时它们的都是在一个线程中按照web.xml中的filter配置的先后顺序执行。
如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的。
如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序。

Filter 的拦截路径

  • 精确匹配
    <url-pattern>/target.jsp</url-pattern>
    以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp
  • 目录匹配
    <url-pattern>/admin/*</url-pattern>
    以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*
  • 后缀名匹配
    <url-pattern>*.html</url-pattern>
    以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
    <url-pattern>*.do</url-pattern>
    以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
    <url-pattern>*.action</url-pattern>
    以上配置的路径,表示请求地址必须以.action 结尾才会拦截到

结论:
Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容