0 什么是Filter
Filter可认为是 Servlet的一种“加强版”,它主要用于对用户请求HttpServletRequest进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可对用户请求生成响应,这一点与 servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给 Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处:
- 在 HttpServletRequest到达 Servlet之前,拦截客户的 HttpServletRequest:根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest头和数据
- 在 HttpServletResponse到达客户端之前,拦截 HttpServletResponse:根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse头和数据
Filter有如下几个种类:
- 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求
- 日志Filter:详细记录某些特殊的用户请求
- 负责解码的Filter:包括对非标准编码的请求解码
- 能改变XML内容的 XSLT Filter等
- Fer可负责拦截多个请求或响应:一个请求或响应也可被多个Fier拦截
创建一个Filter只需两个步骤:
- 创建Filter处理类
- web.xm文件中配置Filter
Filter执行顺序
1 创建 Filter
创建Filter必须实现 javax.servlet.Filter接口,在该接口中定义了如下三个方法,
void init (FilterConfig config)
:用于完成 Filter的初始化void destroy()
:用于Filter销毁前,完成某些资源的回收void doFilter( ServletRequest request, ServletResponse response, Filter Chain chain)
:实现过滤功能,该方法就是对每个请求及响应增加额外的处理
下面是一个日志Filter,负责拦截所有的用户请求,并将请求的信息记录在日志中。
----------------LogFilter.JAVA------;
package lee;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
@WebFilter(filterName="log"
,urlPatterns={"/*"})
public class LogFilter implements Filter {
//FilterConfig可用于访问Filter的配置信息
private FilterConfig config;
//实现初始化方法
public void init(FilterConfig config){
this.config = config;
}
//实现销毁方法
public void destroy(){
this.config = null;
}
//执行过滤的核心方法
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException,ServletException{
//------1---对用户请求执行预处理---------
//获取ServletContext对象,用于记录日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("开始过滤...");
//将请求转换成HttpServletRequest请求
HttpServletRequest hrequest = (HttpServletRequest)request;
//输出提示信息
System.out.println("Filter已经截获到用户的请求的地址: " +
hrequest.getServletPath());
//-----2-----Filter只是链式处理,请求依然放行到目的地址----
chain.doFilter(request, response);
//-----3----对服务器响应执行后处理---------
long after = System.currentTimeMillis();
//输出提示信息
System.out.println("过滤结束");
//输出提示信息
System.out.println("请求被定位到" + hrequest.getRequestURI() +
" 所花的时间为: " + (after - before));
}
}
实现了 doFilter()方法,实现该方法就可实现对用户请求进行预处理,也可实现对服务器响应进行后处理—它们的分界线为是否调用了 chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理
在上面的请求 Filter中,仅在日志中记录请求的URL,对所有的请求都执行 chain.doFilter( request, reponse)方法,当 Filter对请求过滤后,依然将请求发送到目的地址。如果需要检查权限,可以在Filter中根据用户请求的 HttpSession,判断用户权限是否足够。如果权限不够,直接调用重定向即可,无须调用 chain.doFilter(request, reponse方法
2 配置 Filter
前面己经提到,Filter可以认为是 Servlet的“增强版”,因此配置Flter与配置Servlet非常相似,
都需要配置如下两个部分:
- 配置 Filter名
- 配置Filter拦截URL模式。
区别在于,Servlet通常只配置一个URL,而Filter可以同时拦截多个请求的URL。因此,在配置Filter的URL模式时通常会使用模式字符串,使得Filter可以拦截多个请求。
与配置Servlet相似的是,配置Filter 同样有两种方式:
- 在Filter类中通过@WebFilter注解 进行配置。
- 在web.xml文件中通过配置文件进行配置。
上面Filter类的粗体字代码使用@WebFilter 配署该Filter的名字为log,它会拦截向/*发生的所有的请求。
方式1:@WebFilter注解常用属性
属性 | 类型 | 是否必需 | 说明 |
---|---|---|---|
asyncSupported | boolean | 否 | 指定Filter是否支持异步模式 |
dispatcherTypes | DispatcherType[] | 否 | 指定Filter对哪种方式的请求进行过滤。支持的属性:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST;默认过滤所有方式的请求 |
filterName | String | 否 | Filter名称 |
initParams | WebInitParam[] | 否 | 配置参数 |
displayName | String | 否 | Filter显示名 |
servletNames | String[] | 否 | 指定对哪些Servlet进行过滤 |
urlPatterns/value | String[] | 否 | 两个属性作用相同,指定拦截的路径 |
方式2:web.xml
在web.xml文件中配置Filter与配置Servlet非常相似,需要为Filter指定它所过滤的URL,并且可以为Filter配置参数
---------------web.xml---------------
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- 定义Filter -->
<filter>
<!-- Filter的名字,相当于指定@WebFilter
的filterName属性 -->
<filter-name>log</filter-name>
<!-- Filter的实现类 -->
<filter-class>lee.LogFilter</filter-class>
</filter>
<!-- 定义Filter拦截的URL地址 -->
<filter-mapping>
<!-- Filter的名字 -->
<filter-name>log</filter-name>
<!-- Filter负责拦截的URL,相当于指定@WebFilter
的urlPatterns属性 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义Filter -->
<filter>
<!-- Filter的名字 -->
<filter-name>authority</filter-name>
<!-- Filter的实现类 -->
<filter-class>lee.AuthorityFilter</filter-class>
<!-- 下面三个init-param元素配置了三个参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
<init-param>
<param-name>loginPage</param-name>
<param-value>/login.jsp</param-value>
</init-param>
<init-param>
<param-name>proLogin</param-name>
<param-value>/proLogin.jsp</param-value>
</init-param>
</filter>
<!-- 定义Filter拦截的URL地址 -->
<filter-mapping>
<!-- Filter的名字 -->
<filter-name>authority</filter-name>
<!-- Filter负责拦截的URL -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
配置/*代表会拦截所有用户请求。该Filter并未对客户端请求进行额外的处理,仅仅在日志中简要记录请求的信息。
3 常用方法
由于 Filter和 Servlet如此相似,所以Filter和 Servlet具有完全相同的生命周期行为,且 Filter也可以通过<init-param>
元素或@ WebFilter的 initParams属性来配置初始化参数。获取 Filter的初始化参数则使用 FilterConfig的 getInitParameter()方法
下面将定义一个较为实用的Filter,该Filter对用户请求进行过滤,Filter将通过 doFilter方法设置 request编码的字符集,从而避免每个JSP、 Servlet都需要设置;而且还会验证用户是否登录,如果用户没有登录,系统直接跳转到登录页面。
-----------AuthorityFilter.JAVA------
package lee;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
@WebFilter(filterName="authority"
, urlPatterns={"/*"}
, initParams={
@WebInitParam(name="encoding", value="GBK"),
@WebInitParam(name="loginPage", value="/login.jsp"),
@WebInitParam(name="proLogin", value="/proLogin.jsp")})
public class AuthorityFilter implements Filter {
//FilterConfig可用于访问Filter的配置信息
private FilterConfig config;
//实现初始化方法
public void init(FilterConfig config){
this.config = config;
}
//实现销毁方法
public void destroy(){
this.config = null;
}
//执行过滤的核心方法
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException{
//获取该Filter的配置参数
String encoding = config.getInitParameter("encoding");
String loginPage = config.getInitParameter("loginPage");
String proLogin = config.getInitParameter("proLogin");
//设置request编码用的字符集
request.setCharacterEncoding(encoding); //①
HttpServletRequest requ = (HttpServletRequest)request;
HttpSession session = requ.getSession(true);
//获取客户请求的页面
String requestPath = requ.getServletPath();
//如果session范围的user为null,即表明没有登录
//且用户请求的既不是登录页面,也不是处理登录的页面
if( session.getAttribute("user") == null
&& !requestPath.endsWith(loginPage)
&& !requestPath.endsWith(proLogin)){
//forward到登录页面
request.setAttribute("tip" , "您还没有登录");
request.getRequestDispatcher(loginPage).forward(request, response);
}
//"放行"请求
else{
chain.doFilter(request, response);
}
}
}
通过@WebFilter的initParams属性可以为该Filter配置初始化参数,它可以接受多个@WebInitParam,每个@WebInitParam指定一个初始化参数。
在web.xml文件中也使用<init-params…>元素为该Filter配置参数,与配置Servlet初始化参数完全相同
---------------web.xml---------------
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- 定义Filter -->
<filter>
<!-- Filter的名字,相当于指定@WebFilter
的filterName属性 -->
<filter-name>log</filter-name>
<!-- Filter的实现类 -->
<filter-class>lee.LogFilter</filter-class>
</filter>
<!-- 定义Filter拦截的URL地址 -->
<filter-mapping>
<!-- Filter的名字 -->
<filter-name>log</filter-name>
<!-- Filter负责拦截的URL,相当于指定@WebFilter
的urlPatterns属性 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义Filter -->
<filter>
<!-- Filter的名字 -->
<filter-name>authority</filter-name>
<!-- Filter的实现类 -->
<filter-class>lee.AuthorityFilter</filter-class>
<!-- 下面三个init-param元素配置了三个参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
<init-param>
<param-name>loginPage</param-name>
<param-value>/login.jsp</param-value>
</init-param>
<init-param>
<param-name>proLogin</param-name>
<param-value>/proLogin.jsp</param-value>
</init-param>
</filter>
<!-- 定义Filter拦截的URL地址 -->
<filter-mapping>
<!-- Filter的名字 -->
<filter-name>authority</filter-name>
<!-- Filter负责拦截的URL -->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>