Appearance
过滤器
案例目标
- 利用过滤器技术,实现审计功能
- 利用过滤器技术,实现登录权限校验功能
- 利用过滤器技术,实现全局乱码统一处理功能
- 利用过滤器技术,实现脏词过滤功能
过滤器入门
过滤器概述
1.1.1过滤器的作用【重点】
- 过滤 HTTP 交互,即请求和响应
- 对 HTTP 请求和响应进行拦截
- 对 HTTP 请求和响应进行放行
- 对 HTTP 请求和响应进行增强
过滤器的基本原理
过滤器的使用场景
- 脏词过滤
- 审计 - 对 Web 应用的工作效率进行监控
- 登录权限校验
- 全站乱码处理
相关API概要【重点】
过滤器快速入门【重点】
步骤
- 新建普通的 Java 类,实现 Filter 接口。重写 doFilter 方法
- 配置 Filter
- 类路径
- 映射路径
实现
FireEmployeeServlet.java
javapackage com.futureweaver.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * 解雇员工Servlet */ @WebServlet("/admin/fire_employee") public class FireEmployeeServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 因为浏览器字符输出流有可能是中文,所以先设置中文编码 response.setContentType("text/html; charset=utf-8"); // 往浏览器里输出了,要获取浏览器字符输出流 PrintWriter out = response.getWriter(); out.write("<H1>解雇员工Servlet!!!</H1>"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
HireEmployeeServlet
javapackage com.futureweaver.web; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; // 雇佣员工 @WebServlet("/admin/hire_employee") public class HireEmployeeServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 对中文做编集处理 response.setContentType("text/html; charset=utf-8"); // 要给用户一个结果,放到浏览器字符输出流当中 PrintWriter out = response.getWriter(); // 输出 out.write("<H1>雇佣员工Servlet!!!</H1>"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
HelloFilter.java
javapackage com.futureweaver.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; /** * 1. 新建普通的Java类,实现Filter接口,重写doFilter方法 * 2. 配置Filter * 1. 配置类路径 * 2. 配置映射路径 */ public class HelloFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } // 因为Filter是Java EE当中的规范 // 所以,当Filter要拦截请求时,Tomcat会自动为我们调用doFilter方法 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { // 拦截 // 放行 // 增强 // 因为要使用request的getContextPath方法,此方法只在HttpServletRequest当中才会有 // 所以将ServletRequest强转成HttpServletRequest HttpServletRequest request = (HttpServletRequest)servletRequest; HttpServletResponse response = (HttpServletResponse)servletResponse; System.out.println("Filter进入了!!!" + request.getServletPath()); // 如果想要放行的话,那么必须要手动地编写代码 // 手动地编写代码,要放行的话,需要使用filterChain对象 filterChain.doFilter(servletRequest, servletResponse); // 所以没有编写代码的话,就是拦截 System.out.println("Filter完成了!!!" + request.getServletPath()); } @Override public void destroy() { } }
web.xml
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"> <!-- 1. 新建普通的Java类,实现Filter接口,重写doFilter方法 --> <!-- 2. 配置Filter --> <!-- 1. 配置类路径 --> <!-- 2. 配置映射路径 --> <!-- 配置映射路径 --> <filter-mapping> <!-- 为Filter起一个名字 --> <filter-name>HelloFilter</filter-name> <!-- 因为Filter要对资源进行拦截、增强、放行,所以需要配置这一个Filter要拦截哪些资源 --> <!-- /admin/hire... --> <url-pattern>/admin/hire_employee</url-pattern> </filter-mapping> <!-- 配置类路径 --> <filter> <filter-name>HelloFilter</filter-name> <!-- 从映射路径,到了Filter的类路径,那么当浏览器的地址和映射路径匹配到了之后,需要找到相应的Filter类 --> <!-- 因为类在不同的包当中,名字可以是同名的,所以要写绝对路径 --> <!-- 绝对路径就是包名+类名 --> <filter-class>com.futureweaver.filter.HelloFilter</filter-class> </filter> </web-app>
Filter的执行流程分析【重点】
注意:部署描述符非重点。
- Tomcat 启动时会将 web.xml 文件加载到 Tomcat 内存中
- Tomcat 在接收到 Web 请求时,会根据 web.xml 当中指定的资源路径去匹配相应的过滤器。
- 当浏览器地址栏中地址资源匹配到 web.xml 中的映射路径(url-pattern),会通过 web.xml 当中的类路径(filter-class)访问到对应的 Filter 类
- 在 Servlet 的 Java EE 规范当中,Tomcat 接收到 Web 请求时,它会去调用相应的 Filter 类当中的 doFilter 方法
过滤器的生命周期【重点】
编写测试类
java
package com.futureweaver.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
* 1. 新建普通的Java类,实现Filter接口,重写doFilter方法
* 2. 配置Filter
* 1. 配置类路径
* 2. 配置映射路径
*/
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("HelloFilter被创建了!!!");
}
// 因为Filter是Java EE当中的规范
// 所以,当Filter要拦截请求时,Tomcat会自动为我们调用doFilter方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 拦截
// 放行
// 增强
// 因为要使用request的getContextPath方法,此方法只在HttpServletRequest当中才会有
// 所以将ServletRequest强转成HttpServletRequest
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
System.out.println("Filter进入了!!!" + request.getServletPath());
// 如果想要放行的话,那么必须要手动地编写代码
// 手动地编写代码,要放行的话,需要使用filterChain对象
filterChain.doFilter(servletRequest, servletResponse);
// 所以没有编写代码的话,就是拦截
System.out.println("Filter完成了!!!" + request.getServletPath());
}
@Override
public void destroy() {
System.out.println("HelloFilter被销毁了!!!");
}
}
结论
- 项目加载时被 Tomcat 创建
- 项目卸载时被 Tomcat 销毁
- Filter 只创建一次,Filter 线程不安全
过滤器与Web资源的关系
多个Web资源
web.xml
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"> <!-- 1. 新建普通的Java类,实现Filter接口,重写doFilter方法 --> <!-- 2. 配置Filter --> <!-- 1. 配置类路径 --> <!-- 2. 配置映射路径 --> <filter-mapping> <!-- 为Filter起一个名字 --> <filter-name>HelloFilter</filter-name> <!-- 因为Filter要对资源进行拦截、增强、放行,所以需要配置这一个Filter要拦截哪些资源 --> <!-- <url-pattern>/fire_employee</url-pattern> --> <!-- 对多个Web资源进行过滤时,url-pattern可以写多条--> <!-- <url-pattern>/hire_employee</url-pattern> --> <!-- 两个Servlet --> <!-- 这两个Servlet都是有/admin打头 --> <!-- /admin/fire... --> <!-- /admin/hire... --> <url-pattern>/admin/*</url-pattern> </filter-mapping> <filter> <filter-name>HelloFilter</filter-name> <!-- 从映射路径,到了Filter的类路径,那么当浏览器的地址和映射路径匹配到了之后,需要找到相应的Filter类 --> <!-- 因为类在不同的包当中,名字可以是同名的,所以要写绝对路径 --> <!-- 绝对路径就是包名+类名 --> <filter-class>com.futureweaver.filter.HelloFilter</filter-class> </filter> </web-app>
多个过滤器
web.xml
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"> <!-- 配置Filter --> <!-- 配置类路径 --> <!-- 配置映射路径 --> <filter-mapping> <!-- 为Filter起一个名字 --> <filter-name>HelloFilter</filter-name> <!-- 因为Filter要对资源进行拦截、增强、放行,所以需要配置这一个Filter要拦截哪些资源 --> <!-- <url-pattern>/fire_employee</url-pattern> --> <!-- 对多个Web资源进行过滤时,url-pattern可以写多条 --> <!-- <url-pattern>/hire_employee</url-pattern> --> <!-- 两个Servlet --> <!-- 这两个Servlet都是有/admin打头 --> <!-- /admin/fire... --> <!-- /admin/hire... --> <url-pattern>/admin/*</url-pattern> </filter-mapping> <filter> <filter-name>HelloFilter</filter-name> <!-- 从映射路径,到了Filter的类路径,那么当浏览器的地址和映射路径匹配到了之后,需要找到相应的Filter类 --> <!-- 因为类在不同的包当中,名字可以是同名的,所以要写绝对路径 --> <!-- 绝对路径就是包名+类名 --> <filter-class>com.futureweaver.filter.HelloFilter</filter-class> </filter> <!-- 配置HelloFilter2 --> <!-- 配置Filter的映射路径 --> <filter-mapping> <filter-name>HelloFilter2</filter-name> <url-pattern>/admin/hire_employee</url-pattern> </filter-mapping> <!-- 配置Filter的类路径 --> <filter> <filter-name>HelloFilter2</filter-name> <filter-class>com.futureweaver.filter.HelloFilter2</filter-class> </filter> </web-app>
控制台
tex请求被 Filter1 拦截了: http://localhost:8080/day12_war_exploded/my_servlet 请求被 Filter2 拦截了: http://localhost:8080/day12_war_exploded/my_servlet This is My Servlet Page 响应被 Filter2 拦截了: http://localhost:8080/day12_war_exploded/my_servlet 响应被 Filter1 拦截了: http://localhost:8080/day12_war_exploded/my_servlet
过滤器链
多个过滤器组成过滤器链,谁先执行,谁后执行?
在客户端访问服务器 web 资源时,服务器端为一个 web 资源,配置多个过滤器拦截 ,这多个过滤器,就会组成过滤器链 FilterChain, 调用 FilterChain 的 doFilter 表示要执行过滤器链下一个环节,如果当前过滤器已经是链上最后一个过滤器,就会执行目标资源
web 服务器根据 Filter 在 web.xml 文件中的注册顺序<filter-mapping>,决定先调用哪个 Filter
LoginServlet 用户访问是被允许
LoginSuccessServlet 用户直接访问是不被允许
LoginFailureServlet 用户直接访问是不被允许
过滤器深入
过滤器深入
过滤方式配置【重点】
指定 web.xml 当中的 dispatcher 标签
【重点】REQUEST
tex当HTTP请求到相应资源时会被过滤
【重点】FORWARD
tex当转发到相应资源时会被过滤
ERROR
tex当发生错误,展示到错误页面时(web.xml配置<error-page>),会被过滤
INCLUDE
tex当资源被其他页面引入(动态引入)时,会被过滤
过滤器初始化参数配置【了解】
web.xml
xml<filter> <filter-name>Filter1</filter-name> <filter-class>com.futureweaver.filter.Filter1</filter-class> <!-- 配置初始化参数 --> <init-param> <param-name>name</param-name> <param-value>value</param-value> </init-param> </filter>
Filter1.java
javapackage com.futureweaver.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class Filter1 implements Filter { private String nameParam; @Override public void init(FilterConfig filterConfig) throws ServletException { // 取出在web.xml当中配置的参数 // 读取所有的filter初始化参数 Enumeration<String> initParameterNames = filterConfig.getInitParameterNames(); // 遍历初始化参数名字 while (initParameterNames.hasMoreElements()) { // 获取初始化参数名字元素 String s = initParameterNames.nextElement(); System.out.println("初始化参数" + s + ":" + filterConfig.getInitParameter(s)); } } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; System.out.println("请求被 Filter1 拦截了" + request.getRequestURL()); filterChain.doFilter(request, response); System.out.println("响应被 Filter1 拦截了" + request.getRequestURL()); } @Override public void destroy() { } }
小结
Filter 编写的步骤
- 新建一个普通的Java类,实现Filter接口,重写doFilter方法
- 配置Filter
- 类路径
- 映射路径
Filter 生命周期【重点】
项目加载时,由Tomcat创建 项目卸载时,由Tomcat销毁
Servlet 生命周期
第一次被访问,由Tomcat创建 项目卸载时,由Tomcat销毁
Request 生命周期
服务器接收到请求时,由Tomcat创建 响应结束时,由Tomcat销毁
ServletContext 生命周期
项目加载时,由Tomcat创建 项目卸载时,由Tomcat销毁
Session 生命周期
用户第一次访问时,由Tomcat创建 session过期时,由Tomcat销毁
过滤器与 Web 资源的关系
同一个过滤器,对应多个web资源 url-pattern配置多个
- 完全匹配
- 目录匹配
- 后缀名
多个过滤器,对应同一个web资源 多个过滤器其中的url-pattern配置一样的
过滤方式配置
<dispatcher>REQUEST
FORWARD
注解配置过滤器
java
package com.futureweaver.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 1. 新建普通的Java类,实现Filter接口,重写doFilter方法
* 2. 配置Filter
* 1. 配置Filter的类路径
* 2. 配置Filter的映射路径
*/
@WebFilter("/resource_a")
public class AnnotationFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// 当过滤器要进入时,Tomcat会自动地调用doFilter方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 强转
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
System.out.println("AnnotationFilter进入了" + request.getServletPath());
filterChain.doFilter(request, response);
System.out.println("AnnotationFilter离开了" + request.getServletPath());
}
@Override
public void destroy() {
}
}
注解Filter的执行顺序【了解】
- xml 配置方式的 Filter,执行顺序按照在 web.xml 中配置的顺序,从上到下执行;(filter-mapping)
- 注解开发的 Filter,执行的顺序是按照实现类的字母表顺序依次执行!
IDEA 模板
案例
利用过滤器技术,实现审计功能
利用过滤器技术,实现全局乱码统一处理功能
分析
- AuditFilter.java
- EncodingFilter.java
学习目标总结
- 能够独立完成过滤器入门案例
- 能够说出过滤器的生命周期
- 能够阐述 FilterChain 对象的使用
- 能够阐述过滤器的拦截方式
- 过滤器的作用【重点】
- 过滤器的开发步骤【了解】
- 过滤器的生命周期【重点】
- 过滤器链的执行顺序【了解】
- 注解配置 Filter 的执行顺序【了解】