Filter
2023年11月15日大约 10 分钟约 1930 字
三大组件-Filter过滤器

介绍
Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)
Filter 过滤器是 JavaEE 的规范,是接口
image-20220316193401481 Filter 过滤器它的作用是:拦截请求,过滤响应。
应用场景 ● 权限检查 ● 日记操作 ● 事务管理
基本原理

快速入门
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>管理后台登录</title>
</head>
<body>
<h1>管理后台登录</h1>
<%
System.out.println("request=" + request);
%>
<form action="<%=request.getContextPath() %>/loginCheckServlet" method="post">
u:<input type="text" name="username"/> <br/><br/>
p:<input type="password" name="password"/> <br/><br/>
<input type="submit" value="用户登录"/></form>
</body>
</html>
package com.lzw.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 LoginCheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取到用户名和密码
//假设密码是 123456 就可以通过
String username = request.getParameter("username");
String password = request.getParameter("password");
if("123456".equals(password)){
//合法,请求转发到admin.jsp
request.getSession().setAttribute("username", username);
request.getRequestDispatcher("/manage/admin.jsp").forward(request, response);
}else{
//返回登录界面
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>后台管理</title>
<base href="<%=request.getContextPath() %>/manage/"/>
</head>
<body>
<h1>后台管理</h1>
<%
//验证request对象是和前面的filter是一个对象
System.out.println("request=" + request);
%>
<a href="#">用户列表</a>||<a href="#">添加用户</a>||<a href="#">删除用户</a>
<hr/>
<img src="shunping.jpg" height="300"/>
</body>
</html>
package com.lzw.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class ManagerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//当Tomcat 创建 Filter 后,就会调用该方法,进行初始化
System.out.println("ManagerFilter init被调用...");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//当每次调用该filter时,doFilter就会被调用
System.out.println("ManagerFilter doFilter 被调用...");
//如果继续访问目标资源
//在调用过滤器前,request对象已经被创建并封装
//所以,我们这里可以通过 servletRequest 对象获取很多信息,比如访问url,session,参数
//获取到session
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
HttpSession session = httpServletRequest.getSession();
//获取username session 对象,还可以继续使用
Object username = session.getAttribute("username");
if(username != null){
filterChain.doFilter(servletRequest, servletResponse);
//1. 继续访问目标资源url
//2. servletRequest 和 servletResponse 对象会传递给目标资源/文件
//3. 一定要理解filter传递的两个对象,再后面的servlet/jsp 是同一个对象(指的是在一次http请求)
System.out.println("servletRequest=" + servletRequest);
System.out.println("日志信息==");
System.out.println("访问的用户名=" + username.toString());
System.out.println("访问的url=" + httpServletRequest.getRequestURL());
System.out.println("访问的IP=" + httpServletRequest.getRemoteAddr());
filterChain.doFilter(servletRequest, servletResponse);
}else{//说明没有登录过
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
//当filter被销毁时,会调用该方法
System.out.println("ManagerFilter destroy 被调用...");
}
}
<?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 一般写在其他servlet的前面
1. filter配置和servlet配置非常相似,filter也是被tomcat管理和维护
2. url-pattern 就是当请求的url 匹配的时候,就会调用 filter
3. /manage/* 第一个/ 解析成 http://ip:port/工程路径
4. 完整的路径就是 http://ip:port/工程路径/manage/* 当请求的资源url满足该条件时
都会调用 filter
-->
<filter>
<filter-name>ManageFilter</filter-name>
<filter-class>com.lzw.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManageFilter</filter-name>
<url-pattern>/manage/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>LoginCheckServlet</servlet-name>
<servlet-class>com.lzw.servlet.LoginCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginCheckServlet</servlet-name>
<url-pattern>/loginCheckServlet</url-pattern>
</servlet-mapping>
</web-app>
url-pattern
1、url-pattern : Filter 的拦截路径, 即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤
2.、精确匹配 <url-pattern>/a.jsp</url-pattern>
对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截
3、目录匹配 <url-pattern>/manage/ * </url-pattern>
对应的 请求地址 http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截
4、后缀名匹配 <url-pattern>*.jsp</url-pattern>
后缀名可变,比如 *.action *.do 等等对应的请求地址 http://ip[域名]:port/
工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截
5、Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在
Filter生命周期


FilterConfig

介绍
- FilterConfig 是 Filter 过滤器的配置类
- Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
- FilterConfig 对象作用是获取 filter 过滤器的配置内容
<?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-name>LzwFilterConfig</filter-name>
<filter-class>com.lzw.filter.LzwFilterConfig</filter-class>
<!--这里就是该filter配置的参数-由程序员根据业务逻辑来设置-->
<init-param>
<param-name>ip</param-name>
<param-value>127.0</param-value>
</init-param>
<init-param>
<param-name>port</param-name>
<param-value>8888</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LzwFilterConfig</filter-name>
<url-pattern>/abc/*</url-pattern>
</filter-mapping>
</web-app>
package com.lzw.filter;
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
//演示FilterConfig使用
public class LzwFilterConfig implements Filter {
private String ip;//从配置获取的ip
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//通过FilterConfig 获取相关的参数
String filterName = filterConfig.getFilterName();
ip = filterConfig.getInitParameter("ip");
ServletContext servletContext = filterConfig.getServletContext();
//可以获取到该filter所有的配置参数名
Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
//遍历枚举
while(initParameterNames.hasMoreElements()){
System.out.println("名字 = " + initParameterNames.nextElement());
}
System.out.println("filterName = " + filterName);
System.out.println("ip = " + ip);
System.out.println("servletContext = " + servletContext);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//通过 forbidden ip 来进行控制
//先获取到访问ip
String remoteAddr = servletRequest.getRemoteAddr();
if(remoteAddr.contains(ip)) {
System.out.println("封杀该网段..");
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
return;
}
//继续访问目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
FilterChain
介绍
在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链。

应用实例

<?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-name>AFilter</filter-name>
<filter-class>com.lzw.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>BFilter</filter-name>
<filter-class>com.lzw.filter.BFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>BFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
</web-app>
package com.lzw.filter;
import javax.servlet.*;
import java.io.IOException;
public class AFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("AFilter doFilter 的前置代码...");
System.out.println("开始执行 AFilter 的 doFilter()");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("AFilter doFilter 的后置代码...");
}
}
package com.lzw.filter;
import javax.servlet.*;
import java.io.IOException;
public class BFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("BFilter doFilter 的前置代码...");
System.out.println("开始执行 BFilter 的 doFilter()");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("BFilter doFilter 的后置代码...");
}
}
注意事项和细节
- 多个 filter 和目标资源在一次 http 请求,在同一个线程中
- 当一个请求 url 和 filter 的 url-pattern 匹配时, 才会被执行, 如果有多个匹配上,就会顺序执行,形成一个 filter 调用链(底层可以使用一个数据结构搞定)
- 多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象
- 多个 filter 执行顺序,和 web.xml 配置顺序保持一致.
- chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法, 如果后面没有过滤器,则执行目标资源。
- 小结:注意执行过滤器链时, 顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter()-> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 ->返回给浏览器页面/数据