什么是Filter过滤器?
Filter在Web开发中通常被称为过滤器,是一种用于拦截处理请求和响应的组件。它可以在请求到达目标资源之前进行拦截,也可以对响应进行拦截。通过使用过滤器,可以对请求参数进行校验、对响应内容进行压缩和加密等操作,从而提高系统的安全性、性能和可靠性。
过滤器可以串联在处理请求的整个路径中,对请求进行预处理和后处理。例如,可以过滤掉恶意请求、对请求参数进行统一格式化等。在处理响应时,可以统一设置响应头、对响应内容进行压缩等。
- Filter 过滤器它是 JavaWeb 的三大组件之一。
- Filter 过滤器它是 JavaEE 的规范。也就是接口
- Servlet3.0可以用注解@WebFilter,Servlet2.5可以使用xml文件配置。
- Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter("*.do")表示拦截所有以.do结尾的请求
拦截请求常见的应用场景有: 1、权限检查 2、日记操作 3、事务管理 ……等等。
假设有一个admin目录,使用用户登录之后才可以访问这个目录下的资源。
思考:
我们知道,用户登录之后都会把用户登录的信息保存到 Session 域中。所以要检查用户是否登录,可以判断 Session 中否包含有用户登录的信息即可!
<body>
<%
Object user = session.getAttribute("user");
// 如果等于 null,说明还没有登录
if (user == null) {
request.getRequestDispatcher("/login.jsp").forward(request,response);
return;
}
%>
<h2>这是info.jsp</h2>
</body>
结论:
这种在jsp页面方式虽然能判断用户是否登录限制资源访问,但如果在html页面下无法使用这种判断方式,有了局限性,所以最好是使用Filte过滤器限制访问。
Filter工作流程
Filter案例实操
Filter 过滤器的使用步骤:
1、编写一个类去实现 Filter 接口
2、实现过滤方法 doFilter()
3、到 web.xml 中去配置 Filter 的拦截路径
<body>
这是登录页面。login.jsp 页面 <br>
<form action="http://localhost:8080/day05_cookie/login" method="get">
用户名:<input type="text" name="username"/> <br>
密 码:<input type="password" name="password"/> <br>
<input type="submit" />
</form>
</body>
public class AdminFilter implements Filter {
public AdminFilter() {
System.out.println("Filter实现类的构造器初始化操作...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter过滤器初始化操作...");
}
/**
* doFilter():专门用于拦截请求,过滤响应
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
var httpServletRequest = (HttpServletRequest) servletRequest;
var session = httpServletRequest.getSession();
var user = session.getAttribute("user");
if (user == null) {
//如果等于null,则没有登录成功
servletRequest.getRequestDispatcher(File.separator + "login.jsp").forward(servletRequest,servletResponse);
} else {
//登录成功,放行当前请求和响应
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
System.out.println("Filter过滤器销毁操作...");
}
}
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
var username = req.getAttribute("username");
var password= req.getAttribute("password");
if ("admin".equals(username) && "123456".equals(password)) {
req.getSession().setAttribute("user",username);
resp.getWriter().write("登录成功");
} else {
req.getRequestDispatcher(File.separatorChar + "login.jsp").forward(req,resp);
}
}
}
<!-- 配置Filter过滤器 -->
<filter>
<!-- Filter实现类的别名 -->
<filter-name>adminFilter</filter-name>
<!-- Filter实现类的全类名 -->
<filter-class>com.evan.java.AdminFilter</filter-class>
</filter>
<!-- 映射当前请求到Filter实现类 -->
<filter-mapping>
<!-- 当前请求的拦截路径到哪个Filter使用 -->
<filter-name>adminFilter</filter-name>
<!-- 配置拦截路径:
/ 表示请求地址是 http://ip:port/工程路径/ 映射到当前工程模块的web目录
/admin/* 表示请求地址是 http:ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
<!-- 配置servlet访问 -->
<servlet>
<servlet-name>loginServlet</servlet-name>
<servlet-class>com.evan.java.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>loginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
结论:
当login.jsp登录成功时当前请求执行目标资源,Filter检查满足规则则会放行,继续执行下一个操作;当登录失败去执行当前请求的目标资源时会被拦截跳转到login.jsp
Filter生命周期
public class AdminFilter implements Filter {
public AdminFilter() {
System.out.println("1.Filter实现类的构造器初始化操作...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2.Filter过滤器初始化操作...");
}
/**
* doFilter():专门用于拦截请求,过滤响应
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("3.doFilter执行请求和响应的拦截操作...");
}
@Override
public void destroy() {
System.out.println("4.Filter过滤器销毁操作...");
}
}
Filter声明周期的执行过程:
- 执行FIlter实现类的构造器
- 执行Filter初始化方法
- 执行Filter的doFilter拦截方法
- 执行FIlter的销毁方法
步骤1和2是在web工程启动时执行,后续web工程没有关闭,再次执行Filter操作会执行步骤3,步骤3在每次拦截到请求时执行,步骤4在工程停止时执行。
FilterConfig类
FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类。
Tomcat 每次创建 Filter对象的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。
FilterConfig 类的作用是获取 filter 过滤器的配置内容 :
1、获取 Filter 的名称 filter-name 的内容
2、获取在 Filter 中配置的 init-param 初始化参数
3、获取 ServletContext 对象
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter过滤器初始化操作...");
//获取Filter的名称filter-name的内容
System.out.println("filter-name值:" + filterConfig.getFilterName());
//获取web.xml中配置的init-param初始化参数
System.out.println("初始化参数username的值是:" + filterConfig.getInitParameter("username"));
//获取ServletContext对象
System.out.println("ServletContext对象:" + filterConfig.getServletContext());
}
}
<!-- 配置Filter过滤器 -->
<filter>
<!-- Filter实现类的别名 -->
<filter-name>adminFilter</filter-name>
<!-- Filter实现类的全类名 -->
<filter-class>com.evan.java.AdminFilter</filter-class>
<!-- 配置Filter初始化参数值 -->
<init-param>
<param-name>username</param-name>
<param-value>admin</param-value>
</init-param>
</filter>
<!-- 映射当前请求到Filter实现类 -->
<filter-mapping>
<!-- 当前请求的拦截路径到哪个Filter使用 -->
<filter-name>adminFilter</filter-name>
<!-- 配置拦截路径:
/ 表示请求地址是 http://ip:port/工程路径/ 映射到当前工程模块的web目录
/admin/* 表示请求地址是 http:ip:port/工程路径/admin/* 映射到当前工程模块的web目录下的admin目录
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
FilterChain 过滤器链
Filter: 过滤器 。
Chain :链,链条。
FilterChain就是过滤器链条。
多个FilterChain执行的特点:
1、所有Filter和目标资源默认都执行在同一个线程中。
2、多个Filter共同执行的时候,它们都使用同一个Request对象。
FilterChain.doFilter方法的作用:
1、执行下一个Filter过滤器(如果有Filter)
2、执行目标资源(下一步没有Filter执行目标资源)
在多个Filter过滤器执行的时候,他们执行的优先顺序是由他们在web.xml中配置从上而下的顺序决定。
案例实操
//Filter1实现类
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter1 前置代码");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Filter1 后置代码");
}
}
//Filter2实现类
public class Filter2 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter2 前置代码");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("Filter2 后置代码");
}
}
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.evan.java.Filter1</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/filterTest.jsp</url-pattern>
</filter-mapping>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.evan.java.Filter2</filter-class>
</filter>
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/filterTest.jsp</url-pattern>
</filter-mapping>
FilterChain的执行结果:
Filter1 前置代码
Filter1的线程:http-nio-8080-exec-3
Filter2 前置代码
Filter2的线程:http-nio-8080-exec-3
filterText.jsp
Filter2 后置代码
Filter1 后置代码
FIilterChain的执行顺序:
先执行Filter链的所有前置代码以及doFIlter的拦截方法,然后执行Filter链的所有后置代码;同时它们的都是在一个线程中按照web.xml中的filter配置的先后顺序执行。
如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的。
如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序。
Filter 的拦截路径
- 精确匹配
<url-pattern>/target.jsp</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp
- 目录匹配
<url-pattern>/admin/*</url-pattern>
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*
- 后缀名匹配
<url-pattern>*.html</url-pattern>
以上配置的路径,表示请求地址必须以.html 结尾才会拦截到
<url-pattern>*.do</url-pattern>
以上配置的路径,表示请求地址必须以.do 结尾才会拦截到
<url-pattern>*.action</url-pattern>
以上配置的路径,表示请求地址必须以.action 结尾才会拦截到
结论:
Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!