按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
对/admin/路径下的所有请求都会接受 SecurityFilter 的检查,那么
SecurityFilter 里到底做了些什么呢?
public void doFilter(ServletRequest request;
ServletResponse response;
FilterChain chain)
throws IOException; ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req。getSession();
if (session。getAttribute(〃username〃) != null) {
chain。doFilter(request; response);
} else {
res。sendRedirect(〃。。/failure。jsp〃);
}
}
首先要将 ServletRequest 和 ServletResponse 转换成 HttpServletRequest 和
HttpServletResponse,因为Filter 本来设计成为多种协议服务,http 协议仅
仅是其中一部分。不过我们接触到的也只有 http,而且也只有转换成对应
71 / 148
…………………………………………………………Page 72……………………………………………………………
HttpServletRequest 和 HttpServletResponse 才能进行下面的 session 操作和
页面重定向。
得到了 http 请求之后,可以获得请求对应的 session,判断 session 中的
username 变量是否为 null,如果不为 null,说明用户已经登录,就可以调用
doFilter 继续请求访问的资源。如果为 null,说明用户还没有登录,禁止用户
访问,并使用页面重定向跳转到 failure。jsp 页面显示提示信息。
session 中的username 实在登录的时候设置进去的,值就是登录用户使用的用
户名,详细代码可以参考 07…02/WEB…INF/src/LoginServlet。java,登录和注销
都写成了 servlet 并映射到/login。do 和/logout。do 这两个请求路径上。源代码
和 web。xml 配置请自行参考 07…02 中的例子,这里就不复述了。
我们再来看看页面重定向的写法,res。sendRedirect()中使用的是
〃。。/failure。jsp〃,两个点(。。)代表当前路径的上一级路径,这是因为
SecurityFilter 负责处理的是/admin/下的请求,而/failure。jsp 的位置在
/admin/ 目录的上一级,所以加上两个点才能正确跳转到failure。jsp。当然这
里使用 forward()也可以,但是要注意在不同路径下做请求转发会影响页面中相
对路径的指向。相关讨论在:第 3。4。2 节 “forward 导致找不到图片”。
7。3。 filter 所谓的特性
7。3。1。 请求映射
filter…mapping 和 servlet…mapping 都是将对应的 filter 或 servlet 映射到某
个 url…pattern 上,当客户发起某一请求时,服务器先将此请求与 web。xml 中定
义的所有 url…pattern 进行匹配,然后执行匹配通过的 filter 和 servlet。
你可以使用三种方式定义 url…pattern。
1。 直接映射一个请求。
ContactServlet
/contact。do
像第 6。3 节 “使用servlet 改写联系簿”中对 servlet 的映射,只有当
请求是/contact。do 的时候才会执行ContactServlet。/contact。do?id=1
或/contact。do?method=list&id=1 的请求也可以匹配到
ContactServlet,这是因为根据http 规范,请求的路径不包含问号以后
的部分。
72 / 148
…………………………………………………………Page 73……………………………………………………………
2。 映射一个路径下的所有请求。
EncodingFilter
/*
像第 7。1 节 “批量设置请求编码”中这样使用星号(*)的形式,可以
将某个路径下的所有请求都映射到 EncodingFilter 过滤器下,如果这个
路径下还有子路径,那么子路径下的请求也会被 EncodingFilter 过滤。
所以 /*这种写法就会过滤应用下所有的请求。
如果像第 7。2 节 “用filter 控制用户访问权限”中那样把映射配置成
/admin/*,就会只处理/admin/路径下的请求,不会处理根路径下的
/index。jsp 和/failure。jsp。
需要注意的是,这种写法必须以/开头,写成与绝对路径的形式,即便是
映射所有请求也要写成/*,不能简化成*。
3。 映射结尾相同的一类请求。
ControllerServlet
*。do
具体效果请参考 07…03 的例子,index。jsp 中有四个链接,分别指向/a1。do;
/a2。do; /xx/b1。do; /xx/yy/c1。do。
web。xml 中的ControllerServlet 会接收以。do 结尾的请求,并使用
forward 将请求转发到/test。jsp。
点击/a1。do 的情况。
73 / 148
…………………………………………………………Page 74……………………………………………………………
点击/xx/yy/c1。do 的情况。
这样做的一个好处是语义更清楚,只要看到以。do 结尾的请求就知道肯定
是交给 ControllerServlet 处理了,不管这个请求是在根路径还是子路径
下,都会准确无误的找到对应的 servlet。
缺点就是不同路径之间进行 forward,jsp 里就不能再使用相对路径了,
所以我们在 test。jsp 中使用request。getContextPath()获得当前应用在
服务器中的位置(例子中是/07…03)将相对路径都组装成绝对路径,这种
用法在以后也会经常用到。
返回
最后需要注意的是,这种请求映射就不能指定某一路径了,它必须是以星
号(*)开始字母结尾,不能写成/*。do 的形式。
现在咱们也发现 java 的请求映射有多傻了,灵活配置根本是不可能的任务。
想要获得所有以 user 开头。do 结尾的请求吗?user*。do 在 url…pattern 是无法
识别的,只能配置成*。do,再去 servlet 中对请求进行筛选。
想要让一个 servlet 负责多个请求吗?/user/*;/admin/*;*。do 写在一起
url…pattern 也不认识,只能配成多个 servlet…mapping。
74 / 148
…………………………………………………………Page 75……………………………………………………………
ControllerServlet
/user/*
ControllerServlet
/admin/*
ControllerServlet
*。do
java 的复杂性在此处显露无疑。实际使用时,最好不要依赖web。xml 中的配置,
在自己的类中实现灵活配置才是正途。
7。3。2。 过滤链
其实在 07…02 这个例子里,我们使用了两个过滤器,EncodingFilter 负责设置
编码,SecurityFilter 负责控制权限,那这两个过滤器是怎么起作用的呢?它
们两个同时过滤一个请求时谁先谁后呢?
下面这个图会告诉我们答案。
所有的奥秘就在 Filter 中的FilterChain 中。服务器会按照web。xml 中过滤器
定义的先后循序组装成一条链,然后一次执行其中的 doFilter()方法。执行的
顺序就如上图所示,执行第一个过滤器的 chain。doFilter()之前的代码,第二
个过滤器的 chain。doFilter()之前的代码,请求的资源,第二个过滤器的
75 / 148
…………………………………………………………Page 76……………………………………………………………
chain。doFilter()之后的代码,第一个过滤器的 chain。doFilter()之后的代码,
最后返回响应。
因此在 07…02 中执行的代码顺序是:
1。 执行 EncodingFilter。doFilter()中 chain。doFilter()之前的部分:
request。setCharacterEncoding(〃gb2312〃);
2。 执行 SecurityFilter。doFilter()中 chain。doFilter()之前的部分:判断
用户是否已登录。
如果用户已登录,则访问请求的资源:/admin/index。jsp。
如果用户未登录,则页面重定向到:/failure。jsp。
3。 执行 SecurityFilter。doFilter()中 chain。doFilter()之后的部分:这里
没有代码。
4。 执行 EncodingFilter。doFilter()中 chain。doFilter()之后的部分:这里
也没有代码。
过滤链的好处是,执行过程中任何时候都可以打断,只要不执行
chain。doFilter()就不