Java Web之过滤器总结

1.过滤器的概念

过滤器是一个服务器端的组件,它可以拦截客户端的请求和响应信息,并对这些信息进行过滤。

Servlet API中提供了一个Filter接口,如果编写额类实现了这个接口,则称这个类为过滤器。Filter接口源码如下:

package javax.servlet;
import java.io.IOException;
public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;
    public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
    public void destroy();
}

可以看到,接口里有三个抽象方法,分别是init(),doFilter()和destory(),下面我们来详细说一说这三个方法。


2.过滤器的生命周期

  1. 实例化———————————————–web.xml
  2. 初始化———————————————–init()
  3. 执行过滤——————————————–doFilter()
  4. 销毁————————————————–destory()

1.实例化:在web.xml中对过滤器进行配置和映射,也可以使用注解@WebFilter。下面是一种web.xml的示例

<!--配置过滤器--> 
<filter> 
    <filter-name>FilterTest</filter-name> 
    <filter-class>com.codeliu.FilterTest</filter-class> 
</filter> 
<!--映射过滤器--> 
<filter-mapping> 
    <filter-name>FilterTest</filter-name> 
    <!--“/*”表示拦截所有的请求 --> 
    <url-pattern>/*</url-pattern>
    <dispatcher></dispatcher> 
</filter-mapping>

在映射过滤器的时候,有一个dispatcher参数,在Servlet2.5中有四个取值,分别为REQUEST|INCLUDE|FORWARD|ERROR,不填时默认为REQUEST。下面分别来介绍一下这四个参数的作用

REQUEST:当用户直接访问页面时,web容器将会调用过滤器,如果目标资源是通过请求转发(request.getRequestDisPatcher)的include方法或forward方法进行访问,那么该过滤器就不会被调用。

INCLUDE:如果目标资源是通过request.getRequestDisPatcher的include方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。

FORWAED:如果目标资源是通过request.getRequestDisPatcher的forward方法进行访问,那么该过滤器将会被调用,其他情况下,不会被调用。

ERROR:如果目标资源是通过声明或异常处理机制调用,那么该过滤器将会被调用,除此之外,不会被调用。

下面是通过注解的方式

@WebFilter(urlPatterns = {"/*"},  
           initParams = {@WebInitParam(name = "noFilterPath", value = "login.jsp;LoginServlet;fail.jsp", description = "不触发该过滤器的页面"), @WebInitParam(name = "charset", value = "UTF-8")})

2.初始化:web容器创建过滤器实例后将调用init方法,这个方法的参数FilterConfig对象可以获取web.xml文件中过滤器的初始化参数。在Filter的生命周期中,只会初始化一次。

3.执行过滤:这个方法完成实际的过滤操作,当用户访问的URL与web.xml中url-pattern配置的值一样时,就触发这个过滤器,web容器会先调用过滤器,过滤器会调用doFilter方法,这个方法的参数FilterChain对象可以调用doFilter方法,将请求传给下一个过滤器(过滤器链的情况下)或目标资源,也可以利用重定向或转发的方式将请求转发到其他资源。在Filter的生命周期中,可以执行0到n次过滤。

4.销毁:web容器在销毁过滤器实例前调用这个方法(比如stop tomcat),在这个方法中可以释放过滤器占用的资源。在Filter的生命周期中,只会进行一次销毁。


3.过滤器的执行过程

这里写图片描述
上图是一个过滤器链的执行过程,所谓过滤器链,就是当多个过滤器的URL相同时,就形成了过滤器链。那么在过滤器链里每个过滤器的执行顺序是怎么样的呢?有两种情况:

  • 如果你是在web.xml中配置的过滤器,那么执行时就按照你配置的顺进行执行。
  • 如果你是通过注解的方式配置的过滤器,那么执行时就按照首字母的大小进行执行,首字母相同看第二个字母。比如我在过滤器链里一个过滤器为TestFilter,一个过滤器为MyFilter,那显然先执行MyFilter再执行TestFilter。

4.过滤器是怎么进行过滤的?

Filter接口中有一个doFilter方法,每个过滤器都会实现Filter接口,所以每个过滤器都得重写doFilter方法。那么我们就可以在这个方法中编写代码达到以下目的:

  1. 调用目标资源之前,执行某些功能
  2. 是否调用目标资源(即是否让用户访问web资源)
  3. 调用目标资源之后,执行某些功能

过滤全过程:
这里写图片描述
说明一下,在Filter接口中的doFilter方法,形式如下

public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;

如果我们想让用户访问他请求的wen资源,就在doFilter方法中使用如下语句

chain.diFilter(request, response);

使用了该语句,就相当于放行,允许用户访问他想访问的资源,反之就是不允许用户访问。


5.用户登录使用过滤器的小案例

先大致说一下过程。首先是用户输入用户名密码,提交到一个Servlet,我们在这个Servlet的doPost方法里判断用户输入的用户名和密码是否匹配,匹配则跳转到登录成功界面,否则跳转到登录失败界面。那么过滤器在这个过程中起了什么作用呢?我们设想一下,如果用户直接输入登录成功界面的网址,那它是否直接就跳转到了登陆成功界面?或者他输入了其他页面的网址,而这些网址只有用户登录了,才可以进行访问,那又该怎么办呢?我们就可以使用过滤器,如果用户输入了这些网址,我们就进行过滤,跳转到登录界面。

// login.jsp
<form action = "servlet/LoginServlet" method = "post"> 
    用户名:<input type = "text" name = "username"> 
    密   码:<input type = "password" name = "password"> 
    <input type = "submit" value = "submit"> 
</form>

登录界面,一个简单的form表单,将结果发送到Servlet包下的LoginServlet。


// success.jsp
欢迎您,<font color="red">${username}</font>

登录成功页面


// fail.jsp
登陆失败,请检查用户名和密码!

登录失败页面


// LoginServlet里面的doPost方法
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    String username = request.getParameter("username"); 
    String password = request.getParameter("password"); 
    if("admin".equals(username) && "admin".equals(password)) { 
        request.getSession().setAttribute("username", username); 
        //request.getRequestDispatcher(request.getContextPath() + "/success.jsp"); 
        response.sendRedirect(request.getContextPath() + "/success.jsp"); 
    } else { 
        response.sendRedirect(request.getContextPath() + "/fail.jsp"); 
    } 
}

@WebFilter(urlPatterns = {"/*"},  initParams = {@WebInitParam(name = "noFilterPath", value = "login.jsp;LoginServlet;fail.jsp", description = "不触发该过滤器的页面"), @WebInitParam(name = "charset", value = "UTF-8")})
public class LoginFilter implements Filter { 
    // 定义此对象,可以获取初始化参数等 
    private FilterConfig config;
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        HttpServletRequest req = (HttpServletRequest)request; 
        HttpServletResponse res = (HttpServletResponse)response; 
        HttpSession session = req.getSession(); 
        // 获取初始化参数中的路径 
        String paths = config.getInitParameter("noFilterPath"); 
        // 获取字符编码 
        String charSet = config.getInitParameter("charset"); 
        if(charSet == null) { 
            charSet = "UTF-8"; 
        } 
        // 设置字符编码 
        req.setCharacterEncoding(charSet); 
        if(paths != null) { 
            // 将路径进行分割 
            String[] path = paths.split(";"); 
            for(String s:path) { 
                if(s == null || "".equals(s)) { 
                    continue; 
                } 
                // 初始化参数中的路径在请求路径中 
                if(req.getRequestURI().indexOf(s) != -1) { 
                    chain.doFilter(request, response); 
                    return; 
                } 
            } 
        } 
        // 防止直接输入success.jsp进入success.jsp页面 
        if(session.getAttribute("username") != null) { 
            // 如果用户登陆了,放行 
            chain.doFilter(request, response); 
        } else { 
            res.sendRedirect(req.getContextPath() + "/login.jsp"); 
        }
    }
    public void init(FilterConfig fConfig) throws ServletException { 
        config = fConfig; 
    }
}

在Filter的配置中,URL我们使用的是/*,表示匹配所有页面,然后我们又使用了一些初始化参数,通过init方法里的FilterConfig对象获取。可以看到初始化参数里有fail.jsp,login.jsp等,我们获取这些参数后,进行一个判断,如果用户访问的是这些页面,则直接放行。所有该过滤器的功能就是过滤除了初始化参数里其他的所有页面。大家可以想一想为啥这些页面不能进行过滤。


好了,总结到此结束。

猜你喜欢

转载自blog.csdn.net/a_helloword/article/details/80026351