本节内容:

filter的简介
快速入门
filter的API详解
filter的配置
案例一:自动登录
案例二:解决全局代码

一、filter的简介

filter是对客户端访问资源的过滤,符合条件放行,不符合条件不放行,并且可以对目标资源访问前后进行逻辑处理。

二、快速入门

【步骤】:

编写一个过滤器的类实现Filter接口
实现接口中尚未实现的方法(着重实现doFilter方法)
在web.xml中进行配置(主要是配置要对哪些资源进行过滤)

Filter-风君雪科技博客Filter-风君雪科技博客

public class QuickFilter1 implements Filter{
    
    @Override
    //Filter创建的时候执行init方法
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }

    @Override
    //doFilter是Filter的核心过滤的方法
    /*
     * request: 内部封装是客户端http请求的内容
     * response: 代表是响应
     * FilterChain: 过滤器链对象
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        System.out.println("quick1 running....");
        //放行请求。如果不放行,请求将不会到达你想访问的资源
        chain.doFilter(request, response); 
    }

    @Override
    //Filter对象销毁的时候执行destory方法
    public void destroy() {
        System.out.println("destroy...");
    }

}

QuickFilter1.java –过滤器
Filter-风君雪科技博客Filter-风君雪科技博客

public class Servlet1 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        System.out.println("Servlet1 running....");
        response.getWriter().write("Servlet1 running....");
        
    }

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

Servlet1.java –作为被拦截的资源
Filter-风君雪科技博客Filter-风君雪科技博客

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>WEB24</display-name>
  
  <filter>
      <filter-name>QuickFilter1</filter-name>
      <filter-class>com.ithiema.web.filter.QuickFilter1</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>QuickFilter1</filter-name>
      <url-pattern>/*</url-pattern> <!-- 配置对哪些资源拦截 -->
  </filter-mapping> 

  <servlet>
    <description></description>
    <display-name>Servlet1</display-name>
    <servlet-name>Servlet1</servlet-name>
    <servlet-class>com.ithiema.web.servlet.Servlet1</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Servlet1</servlet-name>
    <url-pattern>/servlet1</url-pattern>
  </servlet-mapping>
 </web-app>

web.xml

浏览器访问servlet1资源,在控制台可以看出先走的QuickFilter1,然后在到Servlet1资源。

filter的访问流程:

Filter-风君雪科技博客 

至于图中的哪个filter在前面,根据各个filter在web.xml文件中的位置,注意是看<filter-mapping>的位置,不是看<filter>的位置。

三、filter的API详解

(1)filter生命周期及其与生命周期相关的方法

Filter接口有三个方法,并且这个三个都是与Filter的生命相关的方法

init(Filterconfig):代表filter对象初始化方法。即filter对象创建时执行。
doFilter(ServletRequest,ServletResponse,FilterCha):代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,那么每次访问这个资源都会执行doFilter方法。
destory():代表是filter销毁方法,当filter对象销毁时执行该方法。

Filter对象的生命周期:

Filter何时创建:服务器启动时就创建该filter对象。
Filter何时销毁:服务器关闭时filter销毁。

(2)Filter的AP详解

1)init(FilterConfig)
其中参数config代表 该Filter对象的配置信息的对象,内部封装是该filter的配置信息。

2)destory()方法
filter对象销毁时执行

3)doFilter方法
doFilter(ServletRequest,ServletResponse,FilterChain)
其中的参数:

ServletRequest/ServletResponse:每次在执行doFilter方法时 web容器负责创建一个request和一个response对象作为doFilter的参数传递进来。该request个该response就是在访问目标资源的service方法时的request和response。
FilterChain:过滤器链对象,通过该对象的doFilter方法可以放行该请求。

四、filter的配置

配置示例:

  <filter>
      <filter-name>QuickFilter2</filter-name>
      <filter-class>com.ithiema.web.filter.QuickFilter2</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>QuickFilter2</filter-name>
      <url-pattern>/*</url-pattern> 
  </filter-mapping> 

url-pattern配置:

完全匹配 /sertvle1
目录匹配 /aaa/bbb/*  –最多的

/user/*:访问前台的资源进入此过滤器
/admin/*:访问后台的资源时执行此过滤器

扩展名匹配 *.abc  *.jsp

【注意】:url-pattern可以使用servlet-name替代,也可以混用。比如:

  <filter-mapping>
      <filter-name>QuickFilter2</filter-name>
      <!--<url-pattern>/Servlet1</url-pattern>-->
      <servlet-name>Servlet1</servlet-name> <!-- 和上面那行配置是等效的 -->
  </filter-mapping> 

dispatcher:访问的方式(了解)。dispatcher是可以配置在<filter-mapping>中的属性,没配置的话,它有个默认值。

REQUEST:默认值,代表直接访问某个资源时执行filter。重定向也叫直接访问,只不过是客户端自动去访问的,不是你在浏览器自己输入去访问的。
FORWARD:转发时才执行filter
INCLUDE: 包含资源时执行filter
ERROR:发生错误时、进行跳转时执行filter

  <filter>
      <filter-name>QuickFilter2</filter-name>
      <filter-class>com.ithiema.web.filter.QuickFilter2</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>QuickFilter2</filter-name>
      <url-pattern>/*</url-pattern> 
      <dispatcher>FORWARD</dispatcher>
  </filter-mapping> 

总结Filter的作用:

公共代码的提取
可以对request和response中的方法进行增强(装饰者模式或动态代理)
进行权限控制

五、案例一:自动登录

Filter-风君雪科技博客

自动登录的filter是功能增强的,并不是进行拦截的。带着用户名和密码的cookie,就帮忙自动登上去,没带也放行,该访问谁访问谁。

Filter-风君雪科技博客Filter-风君雪科技博客

public class LoginServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        request.setCharacterEncoding("UTF-8"); //POST提交要写这个,可以在filter里写上这句话,这样每个接收数据的Servlet中就不用写这句话了
                                             //但是这种方式对GET不生效
        
        HttpSession session = request.getSession();
        
        //获取数据
        String username = request.getParameter("username");//中文 张三
        String password = request.getParameter("password");
        
        UserService service = new UserService();
        User user = null;
        try {
            user = service.login(username,password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
        
        if(user!=null){
            //登录成功
            //判断用户是否勾选自动登录,如果登录,将用户名和密码设置进cookie
            String autoLogin = request.getParameter("autoLogin");
            if(autoLogin!=null){
                //对中文张三进行编码,要存入cookie中
                String username_code = URLEncoder.encode(username, "UTF-8");
                
                Cookie cookie_username = new Cookie("cookie_username",username_code);
                Cookie cookie_password = new Cookie("cookie_password",password);
                //设置cookie的持久化时间
                cookie_username.setMaxAge(60*60);
                cookie_password.setMaxAge(60*60);
                //设置cookie的携带路径
                cookie_username.setPath(request.getContextPath());
                cookie_password.setPath(request.getContextPath());
                //发送cookie
                response.addCookie(cookie_username);
                response.addCookie(cookie_password);
            }
            
            //将登录的用户的user对象存到session中
            session.setAttribute("user", user);
            //重定向到首页
            response.sendRedirect(request.getContextPath());
            
        }else{
            //失败 转发到登录页面 提出提示信息
            request.setAttribute("loginInfo", "用户名或密码错误");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
        
    }

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

LoginServlet.java
Filter-风君雪科技博客Filter-风君雪科技博客

public class AutoLoginFilter implements Filter{

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        
        //获得cookie中用户名和密码 进行登录的操作
        //定义cookie_username
        String cookie_username = null;
        //定义cookie_password
        String cookie_password = null;
        //获得cookie
        Cookie[] cookies = req.getCookies();
        if(cookies!=null){
            for(Cookie cookie : cookies){
                //获得名字是cookie_username和cookie_password
                if("cookie_username".equals(cookie.getName())){
                    cookie_username = cookie.getValue();
                    //解码,恢复中文用户名
                    cookie_username = URLDecoder.decode(cookie_username, "UTF-8");
                }
                if("cookie_password".equals(cookie.getName())){
                    cookie_password = cookie.getValue();
                }
            }
        }
        
        //判断username和password是否是null
        if(cookie_username!=null&&cookie_password!=null){
            //登录的代码
            UserService service = new UserService();
            User user = null;
            try {
                user = service.login(cookie_username,cookie_password);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //将登录的用户的user对象存到session中
            session.setAttribute("user", user);
        }
        
        //放行
        chain.doFilter(req, resp);
        
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }

    @Override
    public void destroy() {
        
    }
}

AutoLoginFilter.java
Filter-风君雪科技博客Filter-风君雪科技博客

public class User {

    private int id;
    private String username;
    private String password;
    private String email;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
        
    
}

User.java
Filter-风君雪科技博客Filter-风君雪科技博客

<!-- 自动登录的filter -->
 <filter>
      <filter-name>AutoLoginFilter</filter-name>
      <filter-class>com.ithiema.web.filter.AutoLoginFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>AutoLoginFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

web.xml
Filter-风君雪科技博客Filter-风君雪科技博客

<!-- 登录 注册 购物车... -->
<div class="container-fluid">
    <div class="col-md-4">
        <img src="img/logo2.png" />
    </div>
    <div class="col-md-5">
        <img src="img/header.png" />
    </div>
    <div class="col-md-3" style="padding-top:20px">
        <ol class="list-inline">
            
            <c:if test="${empty user }">
                <li><a href="login.jsp">登录</a></li>
                <li><a href="register.jsp">注册</a></li>
            </c:if>
            <c:if test="${!empty user }">
                <li>欢迎您,${user.username }</li>
                <li><a href="#">退出</a></li>
            </c:if>
            
            <li><a href="cart.jsp">购物车</a></li>
            <li><a href="order_list.jsp">我的订单</a></li>
        </ol>
    </div>
</div>

header.jsp

六、案例二:解决全局的编码

当用户输入中文提交时,无论是POST方式还是GET方式提交,在获取用户提交的数据时都会遇到编码问题。在前面的文章中,对编码的处理都是放在Servlet中,这样凡是Servlet中需要获取用户提交数据的,如果存在中文,都需要处理乱码。

我们可以将编码处理挪到filter中,这样后面的Servlet就不需要单独处理了。

Filter-风君雪科技博客Filter-风君雪科技博客

public class EncodingFilter implements Filter{

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        //request.setCharacterEncoding("UTF-8");
        
        //在传递request之前对request的getParameter方法进行增强
        /*
         * 装饰者模式(包装)
         * 
         * 1、增强类与被增强的类要实现统一接口
         * 2、在增强类中传入被增强的类
         * 3、需要增强的方法重写,不需要增强的方法调用被增强对象的
         * 
         */
        //被增强的对象
        HttpServletRequest req = (HttpServletRequest) request;
        //增强对象
        EnhanceRequest enhanceRequest = new EnhanceRequest(req);
        
        chain.doFilter(enhanceRequest, response);
        
    }

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

class EnhanceRequest extends HttpServletRequestWrapper{ //HttpServletRequest也是实现了HttpServletRequestWrapper
    
    private HttpServletRequest request;

    public EnhanceRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    
    //对getParameter增强,注意并没有对getParameterMap做增强
    @Override
    public String getParameter(String name) {
        String parameter = request.getParameter(name);//乱码
        try {
            parameter = new String(parameter.getBytes("iso8859-1"),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return parameter;
    }
    
}

EncodingFilter.java
Filter-风君雪科技博客Filter-风君雪科技博客

public class EncodingServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        String parameter = request.getParameter("username");//直接获得中文
        
        System.out.println(parameter);
        
    }

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

EncodingServlet.java
Filter-风君雪科技博客Filter-风君雪科技博客

  <!-- 编码统一处理的filter -->
  <filter>
      <filter-name>EncodingFilter</filter-name>
      <filter-class>com.ithiema.web.filter.EncodingFilter</filter-class>
  </filter>                
  <filter-mapping>
      <filter-name>EncodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

web.xml
Filter-风君雪科技博客Filter-风君雪科技博客

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="/WEB24/encodingServlet" method="get">
        <input type="text" name="username">
        <input type="submit" value="提交">
    </form>
</body>
</html>

encoding.jsp