2.9.2 配置Filter

2.9.2 配置Filter

前面已经提到,Filter可以认为是Servlet的“增强版”,因此配置Filter与配置Servlet非常相似,都需要配置如下两个部分。

  1. 配置Filter
  2. 配置Filter拦截URL模式。

区别在于:Servlet通常只配置一个URL,而Filter可以同时拦截多个请求的URL。因此,在配置FilterURL模式时通常会使用模式字符串,使得Filter可以拦截多个请求。

与配置Servlet相似的是,配置Filter同样有两种方式。

  1. Filter类中通过注解进行配置。
  2. web.xml文件中通过配置文件进行配置。

上面Filter类的粗体字代码使用@WebFilter配置该Filter的名字为log,它会拦截向/*发送的所有的请求。

@WebFilter注解属性

@WebFilter修饰一个Filter类,用于对Filter进行配置,它支持如表2.3所示的常用属性。

属性 说明
asyncSupported 指定该Filter是否支持异步操作模式。关于Filter的异步调用请参考2.12节
dispatcherTypes 指定该Filter仅对那种dispatcher模式的请求进行过滤。该属性支持ASYNCERRORFORWARDINCLUDEREQUEST这5个值的任意组合。默认值为同时过滤5种模式的请求
displayName 指定该Filter的显示名
filterName 指定该Filter的名称
initParams 用于为该Filter配置参数
servletnames 该属性值可指定多个Servlet的名称,用于指定该Filter仅对这几个Servlet执行过滤
urlPatterns/value 这两个属性的作用完全相同。都指定该Filter所拦截的URL

web.xml文件中配置Filter与配置Servlet非常相似,需要为Filter指定它所过滤的URL,并且也可以为Filter配置参数
如果不使用注解配置该Filter,则可换成在web.xml文件中为该Filter增加如下配置片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 定义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与配置Servlet非常相似,只是配置Filter时指定url-Pattern/*,即表示该Filter会拦截所有用户请求。该Filter并未对客户端请求进行额外的处理,仅仅在日志中简要记录请求的信息。
为该Web应用提供任意一个JSP页面,并通过浏览器来访问该JSP页面,即可在Tomcat的控制台看到如图2.39所示的信息。

实际上FilterServlet极其相似,区别只是FilterdoFilter()方法里多了一个FilterChain的参数,通过该参数可以控制是否放行用户请求。在实际项目中,FilterdoFilter()方法里的代码就是从多个Servletservice()方法里抽取的通用代码,通过使用Filter可以实现更好的代码复用。
假设系统有包含多个Servlet,这些Servlet都需要进行一些的通用处理:
比如权限控制、记录日志等,这将导致在这些Servletservice()方法中有部分代码是相同的,为了解决这种代码重复的问题,可以考虑把这些通用处理提取到Filter中完成,这样各Servlet中剩下的只是特定请求相关的处理代码,而通用处理则交给Filter完成。图2.40显示了Filter的用途。
由于FilterServlet如此相似,所以FilterServlet具有完全相同的生命周期行为,且Filter也可以通过<init-param>元素或@WebFilterinitParams属性来配置初始化参数,获取Filter的初始化参数则使用FilterConfiggetInitParameter()方法。

下面将定义一个较为实用的Filter,该Filter对用户请求进行过滤,Filter将通过doFilter()方法来设置request编码的字符集,从而避免每个JSPServlet都需要设置;而且还会验证用户是否登录,如果用户没有登录,系统直接跳转到登录页面。

下面是该Filter的源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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);
}
}
}

上面FilterdoFilter()方法开始的三行代码:

1
2
3
String encoding = config.getInitParameter("encoding");
String loginPage = config.getInitParameter("loginPage");
String proLogin = config.getInitParameter("proLogin");

用于获取Filter的配置参数,
代码:

1
request.setCharacterEncoding(encoding);

按配置参数设置了request编码所用的字符集.

接下来的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
}

判断session范围内是否有user属性——没有该属性即认为没有登录,如果既没有登录,而且请求地址也不是登录页和处理登录页,系统直接跳转到登录页面。
通过@WebFilterinitParams属性可以为该Filter配置初始化参数,@WebFilterinitParams属性可以接受多个@WebInitParam,每个@WeblnitParam指定一个初始化参数。

web.xml中为Filter配置参数

web.xml文件中也使用<init-param>元素为该Filter配置参数,与配置Servlet初始化参数完全相同。
如果不使用注解配置该Filter,打算在web.xml文件中配置该Filter,则该Filter的配置片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 定义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指定了三个配置参数,指定loginPage/login.jsp,ProLogic/proLogin.jsp,这表明,如果没有登录该应用,普通用户只能访问/login.jsp/proLogin.jsp页面。只有当用户登录该应用后才可自由访问其他页面。