Day62 Filter过滤器
简介
Filter:过滤器,通过Filter可以拦截访问web资源的请求与响应操作。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器。他可以拦截Jsp、Servlet、 静态图片文件、静态 html文件等,从而实现一些特殊的功能。
例如:实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
服务器发送请求,通过层层过滤器,才能访问资源


创建步骤
- 创建过滤器类并实现Filter接口
- 在web.xml文件中配置Filter
javax.servlet.Filter接口中的方法介绍:
方法 描述 init(FilterConfig fConfig) 初始化方法 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 过滤方法 destroy() 销毁方法 
1.初始Filter过滤器
创建过滤器的步骤:
1.创建Java类,实现Filter接口(javax.servlet.Filter)
2.在web.xml中配置
操作
Welcome.html
导包servlet-api.jar,在web.xml中配置首页
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>欢迎页面</h1>
    
<a href="ser01">向Servlet01发送一个请求</a>
    
</body>
</html>
Filter01
package com.qf.filter;
public class Filter01 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("Filter01 -- doFilter() -- 前");
        //chain过滤器链
        //注意:如果拦截后不调用doFilter(),请求将无法传到下一个过滤器或服务器里
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Filter01 -- doFilter() -- 后");
    }
    @Override
    public void destroy() {
    }
}
Servlet01
package com.qf.servlet;
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet01收到了一个请求");
    }
}
在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">
    <welcome-file-list>
        <welcome-file>Welcome.html</welcome-file>
    </welcome-file-list>
    <filter>
        <filter-name>Filter01</filter-name>
        <filter-class>com.qf.filter.Filter01</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Filter01</filter-name>
        <url-pattern>/*</url-pattern><!-- 拦截所有请求 -->
    </filter-mapping>
    
</web-app>
2.过滤器链
客户端对服务器请求之后,服务器在调用Servlet之前,会执行一组过滤器(多个过滤器),那么这组过滤器就称为一条过滤器链。
过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。

3.Filter的生命周期
3.1生命周期 - 单个过滤器
单个过滤器的生命周期:
 创建:项目启动时 – 无参构造、init()
 销毁:服务器正常关闭时 – destroy()
package com.qf.filter;
public class Filter01 implements Filter {
    public Filter01(){
        System.out.println("Filter01 -- Filter01()");
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String code = filterConfig.getInitParameter("code");
        System.out.println("Filter01 -- init() -- " + code);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter01 -- doFilter() -- 前");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("Filter01 -- doFilter() -- 后");
    }
    @Override
    public void destroy() {
        System.out.println("Filter01 -- destroy()");
    }
}
在web.xml配置文件中配置初始化参数信息 code-UTF-8
<?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">
    <welcome-file-list>
        <welcome-file>Welcome.html</welcome-file>
    </welcome-file-list>
    <filter>
        <filter-name>Filter01</filter-name>
        <filter-class>com.qf.filter.Filter01</filter-class>
        <init-param>
            <param-name>code</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Filter01</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
运行结果:

3.2 生命周期 - 多个过滤器
Filter的生命周期:
 创建:项目启动时 – 无参构造、init()
 销毁:服务器正常关闭时 – destroy()
Filter的创建顺序 – 不用关注:
 多线程抢资源(创建顺序无序)
Filter的调用顺序 – 关注:
 web.xml配置 ------> 按照配置顺序
 @WebFilter配置 --> 按照类名的字典排序
经验:一般使用web.xml配置
是单例模式
//----------- Filter01 -----------------------
public class Filter01 implements Filter {
    public Filter01() {
    	System.out.println("Filter01 - Filter01()");
    }
    public void init(FilterConfig fConfig) throws ServletException {
    	System.out.println("Filter01 - init()");
	}
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    	System.out.println("Filter01执行前");
    	chain.doFilter(request, response);//放行
    	System.out.println("Filter01执行后");
	}
    
	public void destroy() { 
		System.out.println("Filter01 - destroy()");
	}
}
//----------- Filter02 -----------------------
public class Filter02 implements Filter {
	public Filter02() {
		System.out.println("Filter02 - Filter02()");
	}
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("Filter02 - init()");
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("Filter02执行前");
		chain.doFilter(request, response);//放行
		System.out.println("Filter02执行后");
	}
	public void destroy() { 
		System.out.println("Filter02 - destroy()");
	}
}
//----------- Filter03 -----------------------
public class Filter03 implements Filter {
	public Filter03() {
		System.out.println("Filter03 - Filter03()");
	}
	public void init(FilterConfig fConfig) throws ServletException {
		System.out.println("Filter03 - init()");
	}
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("Filter03执行前");
		chain.doFilter(request, response);//放行
		System.out.println("Filter03执行后");
	}
	public void destroy() { 
		System.out.println("Filter03 - destroy()");
	}
}
2.在web.xml配置文件中配置过滤器信息
<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">
    
    <welcome-file-list>
        <welcome-file>welcome.html</welcome-file>
    </welcome-file-list>
    
         <filter> 
             <filter-name>Filter01</filter-name> 
             <filter-class>com.qf.filter.Filter01</filter-class> 
             <init-param> 
                 <param-name>code</param-name> 
                 <param-value>UTF-8</param-value> 
             </init-param> 
         </filter> 
         <filter-mapping> 
             <filter-name>Filter01</filter-name> 
             <url-pattern>/*</url-pattern> 
         </filter-mapping> 
    
         <filter> 
             <filter-name>Filter02</filter-name> 
             <filter-class>com.qf.filter.Filter02</filter-class> 
             <init-param> 
                 <param-name>code</param-name> 
                 <param-value>UTF-8</param-value> 
             </init-param> 
         </filter> 
         <filter-mapping> 
             <filter-name>Filter02</filter-name> 
             <url-pattern>/*</url-pattern> 
         </filter-mapping> 
    
         <filter> 
             <filter-name>Filter03</filter-name> 
             <filter-class>com.qf.filter.Filter03</filter-class> 
             <init-param> 
                 <param-name>code</param-name> 
                 <param-value>UTF-8</param-value> 
             </init-param> 
         </filter> 
         <filter-mapping> 
             <filter-name>Filter03</filter-name> 
             <url-pattern>/*</url-pattern> 
         </filter-mapping> 
    
</web-app>
3.3 注解配置过滤器(简化配置)
创建顺序:创建顺序无序
执行顺序:按照类名的顺序执行
注意:一般不使用这个
@WebFilter(value="/*",initParams={@WebInitParam(name="encode",value="UTF-8")})
public class EncodeFilter implements Filter {
    ...
}
4.案例

4.1 案例一:编码过滤器
解决请求和响应乱码问题
package com.qf.filter;
public class CodeFilter implements Filter {
    private String code;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //获取web.xml中该过滤器的初始化属性
         code = filterConfig.getInitParameter("code");
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding(code);
        servletResponse.setContentType("text/html;charset="+code);
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {
    }
}
   <filter>
        <filter-name>CodeFilter</filter-name>
        <filter-class>com.qf.filter.CodeFilter</filter-class>
        <init-param>
            <param-name>code</param-name><!-- 初始化参数名 -->
            <param-value>UTF-8</param-value><!-- 初始化参数值 -->
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CodeFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
4.2 案例二:登录权限过滤器
解决权限的统一控制问题,没有登录,就不能直接跳转到其他详情页面
package com.qf.filter;
public class LoginFilter implements Filter {
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
   }
   @Override
   public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       //强转
       HttpServletRequest request= (HttpServletRequest) servletRequest;
       HttpServletResponse response= (HttpServletResponse) servletResponse;
       //获取请求地址
       String uri = request.getRequestURI();
       
       if (uri.equals("/Day19_war_exploded/")   ||
               uri.contains("register.jsp")   ||
               uri.contains("RegisterServlet")   ||
               uri.contains("welcome.html")   ||
               uri.contains("login.jsp")   ||
               uri.contains("CodeServlet")   ||
               uri.contains("LoginServlet") ){
           filterChain.doFilter(request,response);
       }else {
           //如果登录成功,会存储session凭证
           HttpSession session = request.getSession();
           String username = (String) session.getAttribute("username");
           String name = (String) session.getAttribute("name");
           String role = (String) session.getAttribute("role");
           if (username==null||name==null||role==null){//session凭证为空,不允许登录
               response.sendRedirect("login.jsp");
           }else {
               if(!role.equals("teacher") && uri.contains("GetStuListServlet")){
                   response.sendRedirect("login.jsp");
               }else{
                   filterChain.doFilter(request,response);
               }
           }
       }
   }
   @Override
   public void destroy() {
   }
}
    <filter>
        <filter-name>LoginFilter</filter-name>
        <filter-class>com.qf.filter.LoginFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>LoginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
4.3 案例三:关键字过滤器
解决文档内的一个敏感词汇
Welcome.html,提交建议
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <a href="proposal.jsp">提交建议</a>
</body>
</html>
proposal.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    
<h1>建议页面</h1>
<form action="ProposalServlet" method="post">
    建议:<input type="text" name="info"><br>
    <input type="submit" value="提交"/>
    <input type="button" value="返回" οnclick="goIndex()"/>
</form>
<script type="text/javascript">
    function goIndex(){
        window.location = "Welcome.html";
    }
</script>
</body>
</html>
MyHttpServletRequestWrapper
请求包装类,处理敏感词
package com.qf.wrapper;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
    //有参构造
    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    //子类重写
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        //把尖括号替换成字符尖括号,替换后不会认为是html里的尖括号符号
        value = value.replaceAll("<", "<");
        value = value.replaceAll(">", ">");
        value = value.replaceAll("傻逼", "**");
        return value;
    }
}
SensitiveWordsFilter
package com.qf.filter;
public class SensitiveWordsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //创建请求包装类对象(注意:对象中包含了请求对象)
        MyHttpServletRequestWrapper myHttpServletRequestWrapper = new MyHttpServletRequestWrapper(request);
        //放行的是请求包装类对象
        filterChain.doFilter(myHttpServletRequestWrapper,response);
    }
    @Override
    public void destroy() {
    }
}
    <filter>
        <filter-name>SensitiveWordsFilter</filter-name>
        <filter-class>com.qf.filter.SensitiveWordsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SensitiveWordsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
运行结果:


总结
Filter
概念
理解:过滤器链
过滤器的生命周期
案例















![[Docker][Docker NetWork][下]详细讲解](https://i-blog.csdnimg.cn/direct/f996e9bc2d0e4f16b6d23d1c71f8df63.png)


