一、过滤器简介
1. 什么是过滤器
Filter(过滤器)是依赖于 Servlet 容器运行的,开发人员通过该技术,可以实现用户在访问某个目标资源之前,用来过滤用户的请求和响应,修改用户的请求和响应的数据,对请求进行拦截。如果某个功能是很多Web资源都需要执行,我们就可以在过滤器中集中进行处理。
2. 过滤器应用场景
- 统一解决 post 提交请求乱码
- 登录权限检查
- 资源访问权限控制
- 过滤用户发表内容中的非法字符
二、创建 First Filter Demo
1. 使用 Filter步骤
- 创建一个类实现过滤器接口
- 注解配置过滤器拦截的请求路径
- 在
doFilter()
中编写过滤操作 FilterChain.doFilter()
放行
2. 代码
使用注解配置Filter,也可把配置信息放在 web.xml。
package com.zz.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//编写过滤的路径: urlPatterns过滤的路径,只有访问welcome的时候过滤器才会执行。
@WebFilter(filterName = "FilterDemo",urlPatterns = "/welcome")
public class FilterDemo implements Filter {
//编写过滤任务的代码
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
System.out.println("过滤器执行了...");
//如果您允许客户访问目标资源
chain.doFilter(request,response); //放行..
}
public void init(FilterConfig config) throws ServletException {
System.out.println("过滤器执初始化");
}
public void destroy() {
System.out.println("过滤器销毁了");
}
}
3. 工作原理
当过滤器类被载入容器并实例化后,容器会运行其init()
方法并传入 FilterConfig对象作为参数。FilterConfig 是实现Filter接口的类上使用标注或web.xml中过滤器设置信息的代表对象,如果在定义过滤器时设置了初始参数,则可以通过getInitParameter()
方法来取得初始参数。
Filter接口的 doFilter()
方法类似于Servlet接口的service方法,当请求来到容器,而在 Servlet的service方法 执行之前,就调用该过滤器的 doFilter()
方法。
调用了FilterChain的 doFilter()
方法,就会运行下一个过滤器,如果没有下一个过滤器,就会调用请求目标 Servlet的service方法。如果因为某个情况(如用户没有通过验证)而没有调用 FilterChain的doFilter方法,则请求就不会继续交给接下来的过滤器或目标Servlet,这时就是所谓的拦截请求。
三、过滤器的生命周期
过滤器的生命周期包含三个方法:
1. init()
init方法是初始化方法,当过滤器被部署到web服务器时,服务器启动时执行一次。
2. doFilter()
只要有一个请求符合Filter拦截路径,都会执行doFilter方法。
3. destroy()
destroy方法是过滤器的销毁方法,服务器停止或者将项目从服务器中移除时,执行destroy方法销毁过滤器。
4. 生命周期总结
init()
:服务器启动时会执行一次doFilter()
:每次有符合过滤路径的请求时,都会重复执行destroy()
:服务器停止或者项目从服务器中移除时执行一次
四、映射路径
1. 过滤路径
配置过滤器不同的映射路径,从而让过滤器过滤希望过滤器的请求。
@WebFilter(filterName = "FilterDemo",urlPatterns = "/xxx")
2. 过滤方式
匹配方式 | 示例 | 含义 |
---|---|---|
全局 | /* | 整个应用的配置,访问所有资源时都起作用 |
以指定资源匹配 | /index.html | 只有访问了index.html 资源时过滤器才会起作用 |
以指定目录匹配 | /servlet/* | 只有访问了指定目录servlet 下的资源时过滤器才会起作用 |
以后指定后缀名匹配 | *.jsp | 只有访问了指定后缀为.jsp 的资源时过滤器才会起作用 |
3. 单个Filter过滤多种请求
如果一个过滤器需要过滤多种文件,则可在web.xml中配置多个<filter-mapping>
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.zz.control.loginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/login.jsp</url-pattern>
</filter-mapping>
五、过滤器链
1. 什么是过滤器链
过滤器链: 访问某个资源的时候会经过多个过滤器
2. 多个Filter的执行顺序
在请求到达目标Servle之前是可以经过多个过滤器的。一般来说,建议Filter之间不要有关联,各自处理各自的逻辑即可,这样无需关心执行顺序问题。如果要考虑执行顺序,使用注解配置的话,Filter的执行顺序跟名称的字母顺序有关,例如 AFilter 会比 BFilter 先执行;使用配置文件 web.xml 时Filter的执行顺序跟 <filter-mapping>
的顺序有关,先声明的先执行。如下:BFilter比AFilter先执行。
<!--配置BFilter的基本信息-->
<filter>
<!--filter的别名-->
<filter-name>BFilter</filter-name>
<!--filter的完整类名-->
<filter-class>com.zz.filter.BFilter</filter-class>
</filter>
<filter-mapping>
<!--filter的别名-->
<filter-name>BFilter</filter-name>
<!--过滤的路径-->
<url-pattern>/login.jsp</url-pattern>
</filter-mapping>
<!--配置AFilter的基本信息-->
<filter>
<filter-name>AFilter</filter-name>
<filter-class>com.zz.filter.AFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AFilter</filter-name>
<url-pattern>/index.html</url-pattern>
</filter-mapping>
注意:在web.xml中配置过滤器时,监听器 > 过滤器 > servlet
,也就是说web.xml中监听器配置在过滤器之前,过滤器配置在servlet之前,否则会出错。
六、拦截方式
1. 学习过滤器的拦截方式
Filter默认情况只会过滤 URL地址直接访问 的与 请求重定向 的资源,对于 请求转发 的资源过滤器默认是不拦截,如果需要拦截请求转发的资源,需要学习过滤器的拦截方式。
2. 两种拦截方式
-
request(默认的拦截方式):浏览器发出的请求都会进行拦截,1)直接从地址栏访问的资源; 2)请求重定向的资源;
-
forward: 拦截请求转发的资源。
代码如下:
/*
dispatcherTypes : 用于配置拦截方式的。
1.request 是默认的方式, 过滤 url地址直接访问的 与 请求重定向 的资源
2.forward 过滤 请求转发 的资源
*/
@WebFilter(filterName = "DispatcherFilterDemo",urlPatterns = "/index.html",
dispatcherTypes={DispatcherType.FORWARD,DispatcherType.REQUEST} )
public class DispatcherFilterDemo implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("过滤器执行了..");
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
}
七、登录验证案例
用过滤器实现登录验证,没登录则驳回访问请求并重定向到登录页面
public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) srequest;
HttpServletResponse response = (HttpServletResponse) sresponse;
HttpSession session = request.getSession();
String path = request.getRequestURI();
Integer uid = (Integer) session.getAttribute("userid");
// 登录页面不过滤
if (path.indexOf("/login.jsp") > -1) {
// 递交给下一个过滤器
chain.doFilter(srequest, sresponse);
return;
}
// 注册页面不过滤
if (path.indexOf("/register.jsp") > -1) {
chain.doFilter(request, response);
return;
}
// 已经登录
if (uid != null) {
// 放行,递交给下一个过滤器
chain.doFilter(request, response);
} else {
response.sendRedirect("login.jsp");
}
}