前言

感谢

Filter介绍

我们在登录网站的时候,经常会遇到这种情况:我们想要对某个信息进行评论,却被告知要先登录。

我们想要看某个电影,读一本什么小说,却要求要开会员,购买等等。

也就是说,不达到一定的条件,这些资源我们是不能进行交互的。

我们之前学习的某些知识也可以实现这些内容,比如对比某个用户的session信息,然后到数据库去查看这些权限等等。但是过于复杂。

接下来我们要讲解的内容叫做过滤器Filter。过滤器可以十分方便的实现这些内容。

在访问服务器上的资源的时候,会首先通过过滤器,假如过滤器要求放行,那我们就要放行,加入过滤器不允许,那就拦截。

不仅于此,过滤器因为是在访问资源之前执行的,还可以在过滤器中统一编码字符集等

那么总结一些过滤器可以做到的事情:

1、登录验证

2、统一编码处理

3、敏感字符过滤

4、其他

快速起步

之前创建一个JavaWeb的项目就不讲了,我们直接上正菜

1、定义一个类,实现接口 javax.servlet.Filter

2、复写方法

  1. package com.howling;
  2. import javax.servlet.*;
  3. import java.io.IOException;
  4. public class HelloFilter implements Filter {
  5. /**
  6. * 初始化,当web服务器启动的时候进行初始化
  7. *
  8. * @param filterConfig
  9. * @throws ServletException
  10. */
  11. public void init(FilterConfig filterConfig)
  12. throws ServletException {
  13. System.out.println("Filter初始化,随时等待请求....");
  14. }
  15. /**
  16. * 进行过滤操作
  17. * 过滤配置的请求,chain:链
  18. * 让请求继续通行:chain.diFilter(request,response)
  19. *
  20. * @param servletRequest
  21. * @param servletResponse
  22. * @param filterChain
  23. * @throws IOException
  24. * @throws ServletException
  25. */
  26. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
  27. throws IOException, ServletException {
  28. System.out.println("过滤请求......");
  29. filterChain.doFilter(servletRequest, servletResponse);
  30. }
  31. /**
  32. * 销毁Filter...
  33. */
  34. public void destroy() {
  35. System.out.println("Filter销毁......");
  36. }
  37. }

其实说老实话,看这个方法名字,作用其实就能猜到差不多,一个好的方法名字就应该是这样的

3、配置拦截路径,可以使用web.xml或者注解的方式

  • 使用web.xml方式实现
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0"
  6. metadata-complete="false">
  7. <filter>
  8. <filter-name>FirstFilter</filter-name>
  9. <!--FirstFilter所对应的全类名-->
  10. <filter-class>com.howling.HelloFilter</filter-class>
  11. </filter>
  12. <filter-mapping>
  13. <filter-name>FirstFilter</filter-name>
  14. <!--在进行任何请求的时候,都会走过滤器FirstFilter-->
  15. <url-pattern>/*</url-pattern>
  16. </filter-mapping>
  17. </web-app>
  • 使用注解的方式实现,只需要在过滤器上添加一个注解即可
package com.howling;

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

@WebFilter("/*")
public class HelloFilter implements Filter {


    public void init(FilterConfig filterConfig)
            throws ServletException {

        System.out.println("Filter初始化,随时等待请求....");

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("过滤请求......");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    public void destroy() {
        System.out.println("Filter销毁......");
    }
}

不用编写页面,直接随意访问一个路径,看看它是否能够进行过滤即可 当然了,如果你是根据原型创建的框架,现在应该已经有了一个默认的index.jsp

4、进行请求

过滤器 - 图1 在控制台中我们可以验证到:在服务器启动的时候Filter就已经进行了初始化,然后在访问资源的时候进行了过滤操作,最后关闭服务器的时候进行了Filter的销毁


拦截器配置详解

拦截请求

1、拦截具体资源:/index.jsp:只有访问index.jsp时,才会进行拦截

2、拦截目录:/user/*:只有访问/user/xxx时的资源时,才会进行拦截

3、后缀名拦截:*.jsp*:只有访问jsp资源时,才会进行拦截

4、拦截所有:/*:访问所有资源都会拦截

注解拦截

dispatcherTypes:可以设置多个值,这多个值其实是枚举

1、REQUEST:默认值,浏览器直接请求资源 只要发起的请求是一次HTTP请求,就走几次过滤器。重定向相当于走了两次请求 2、FORWARD:转发访问资源 只有当前页面是通过请求转发过来的场景,才会走指定的过滤器 3、INCLUDE:包含访问资源 只要JSP是通过<jsp:include page="xxx.jsp">嵌入进来的界面,每嵌入一个界面,都会走一次指定的过滤器 4、ERROR:错误跳转资源 假如在web.xml中配置了<error-page> 这个意思是HTTP的响应状态吗只要是错误码之一,就会将请求转发到error.jsp下,这就触发了一次error,然后就走error过滤器 虽然这个过程是转发到error.jsp的过程,但是并不会走转发过滤器 一般来说,我们一般将404,400,500设为错误码 5、ASYNC:异步访问资源 访问一步资源的时候进行过滤

xml拦截

dispatcherTypes:必须写在filter-mapping的最后


接下来我将使用xml的形式进行配置,但是先不演示异步

1、首先是几个JSP页面

index.jsp

<html>
    <body>
        <h2>Hello World!</h2>
    </body>
</html>

include的页面

  • A.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:include page="B.jsp"/>
<html>
    <head>
        <title>A</title>
    </head>
    <body>
        <h1>A</h1>
    </body>
</html>

一般来说,一个JSP引入另外一个JSP有两种方式,一种就是我们的指令:<%@include file="./B.jsp" %> 另一种就是JSP的动作:<jsp:include page="B.jsp"/> 这两种方式还是有区别的 1、第一种方式,是在jsp被转换为Servlet之前将引入的代码插入进去 2、第二种方式,是在JSP在被请求的时候将插入的页面引入进来 所以他们因为调用的时间不同,从而导致第一种方式不能被过滤器识别,只会执行request过滤器动作

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>B</title>
    </head>
    <body>
        <h1>B</h1>
    </body>
</html>

转发页面

  • redirection.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>redirection</title>
</head>
<body>
    <h1>redirection</h1>
</body>
</html>

重定向页面

  • repost.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>repost</title>
</head>
<body>
    <h1>repost</h1>
</body>
</html>

错误页面

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

    <h1>ERROR</h1>

</body>
</html>

2、几个Servlet

package com.howling.servlet;

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

public class RedirectionServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        req.getRequestDispatcher("/redirection/redirection.jsp").forward(req,resp);

    }
}
package com.howling.servlet;

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

public class RepostServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("/repost/repost.jsp");
    }
}

只写了转发和重定向的界面,这个非常合理,因为只有这两个需要Servlet的测试


3、几个过滤器

  • 请求过滤器
package com.howling.filter;

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

public class RequestFilter 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("requestFilter执行...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • 包含过滤器
package com.howling.filter;

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

public class IncludeFilter 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("includeFilter执行...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • 错误过滤器
package com.howling.filter;

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

public class ErrorFilter 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("errorFilter执行...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • 转发过滤器
package com.howling.filter;

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

public class RedirectionFilter 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("redirectionFilter执行...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • 重定向过滤器
package com.howling.filter;

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

public class RepostFilter 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("responseFilter执行...");
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

4、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"
         metadata-complete="false">

    <!--配置错误页面-->
    <error-page>
        <error-code>400</error-code>
        <location>/error.jsp</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error.jsp</location>
    </error-page>

    <!--请求过滤器,浏览器直接请求资源时进行拦截,重定向也算一次请求-->
    <!--跳转错误资源时执行过滤器-->
    <filter>
        <filter-name>error</filter-name>
        <filter-class>com.howling.filter.ErrorFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>error</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <!--请求时执行过滤器-->
    <filter>
        <filter-name>request</filter-name>
        <filter-class>com.howling.filter.RequestFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>request</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <!--转发时执行过滤器-->
    <filter>
        <filter-name>forward</filter-name>
        <filter-class>com.howling.filter.RedirectionFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>forward</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <!--包含时执行过滤器-->
    <filter>
        <filter-name>include</filter-name>
        <filter-class>com.howling.filter.IncludeFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>include</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>


    <!--配置servlet映射-->
    <servlet>
        <servlet-name>redirectionServlet</servlet-name>
        <servlet-class>com.howling.servlet.RedirectionServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>redirectionServlet</servlet-name>
        <url-pattern>/redirect</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>repostServlet</servlet-name>
        <servlet-class>com.howling.servlet.RepostServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>repostServlet</servlet-name>
        <url-pattern>/repost</url-pattern>
    </servlet-mapping>

</web-app>

5、最后一步,就是发起请求来测试了

为了方便,我把项目的结构贴出来

过滤器 - 图2


过滤器链

过滤器执行的顺序为

1、注解配置:按照类名字符串排序,值小的先执行

2、web.xml配置:谁的<filter-mapping>在上面,谁先执行

举个例子,我在上面的过滤器中,第一个是request,然后是其他。 假设现在我们要访问一个资源,第一个永远是request,然后才是其他的内容

但是在这里我十分不推荐这种顺序 假如我现在要访问一个不存在的资源,还是会首先执行requestfilter,然后执行errorfilter 假如我的策略是,在执行errorfilter的时候就将这个请求拦截,那么请求requestfilter岂不是多此一举

所以我在使用过滤器的一般原则是:范围越小,针对越独特的放在前面;范围大,适用范围越广的放在后面 当然,这个也要根据具体的业务来判断

过滤器到资源的路径为

现在我们有两个过滤器A和B。假设我们首先执行A,然后执行B

1、访问资源的顺序:A—>B—>资源

2、从资源返回的顺序:资源—>B—>A

比如说:

public class TestFilter2 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
        throws IOException, ServletException {
        System.out.println("2 before");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("2 after");
    }
}
public class TestFilter1 implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
        throws IOException, ServletException {
        System.out.println("1 before");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("1 after");

    }
}
@WebServlet("/test")
public class Test extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("servlet...");
        resp.getWriter().write("servlet");
    }
}
最终结果:
1 before
2 before
servlet...
2 after
1 after