`
burnquist
  • 浏览: 69105 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类

第7章 使用filter过滤请求

    博客分类:
  • WEB
阅读更多
第 7 章 使用filter过滤请求

注意
Filter虽然很常用,但是覆盖的范围太广,这里我们只介绍设置编码和控制权限的过滤器,其他的使用方式还需要大家自行积累。
如果你不满足以下任一条件,请继续阅读,否则请跳过此后的部分,进入下一章:第 8 章 配置listener监听器。
了解Filter的使用。

7.1. 批量设置请求编码
编码问题会不会成为中国人学java的标志呢?

通过之前的讨论第 2.2.2 节 “POST乱码”,我们知道为了避免提交数据的乱码问题,需要在每次使用请求之前设置编码格式。在你复制粘贴了无数次request.setCharacterEncoding("gb2312");后,有没有想要一劳永逸的方法呢?能不能一次性修改所有请求的编码呢?

用Filter吧,它的名字是过滤器,可以批量拦截修改servlet的请求和响应。
我们编写一个EncodingFilter.java,来批量设置请求编码。

package anni;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {

    public void init(FilterConfig config) throws ServletException {}

    public void destroy() {}

    public void doFilter(ServletRequest request,
            ServletResponse response,
            FilterChain chain)
            throws IOException, ServletException {

        request.setCharacterEncoding("gb2312");
        chain.doFilter(request, response);
    }

}

       
在此EncodingFilter实现了Filter接口,Filter接口中定义的三个方法都要在EncodingFilter中实现,其中doFilter()的代码实现主要的功能:为请求设置gb2312编码并执行chain.doFilter()继续下面的操作。

与servlet相似,为了让filter发挥作用还需要在web.xml进行配置。


<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>anni.EncodingFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

       
filter标签部分定义使用的过滤器,filter-mapping标签告诉服务器把哪些请求交给过滤器处理。这里的/*表示所有请求,/表示根路径,*(星号)代表所有请求,加在一起就变成了根路径下的所有请求。

这样,所有的请求都会先被EncodingFilter拦截,并在请求里设置上指定的gb2312编码。

例子在lingo-sample/07-01目录下,这次我们不需要在test.jsp中为请求设置编码也可以得到正常的中文参数了,EncodingFilter圆满的完成了它的工作。

7.2. 用filter控制用户访问权限
出于信息安全和其他一些原因的考虑,项目中的一些页面要求用户满足了一定条件之后才能访问。比如,让用户输入帐号和密码,如果输入的信息正确就在session里做一个成功登录的标记,其后在请求保密信息的时候判断session中是否有已经登录成功的标记,存在则可以访问,不存在则禁止访问。

如07-02例子中所示,进入首页看到的就是登录页面。



现在用户还没有登录,如果直接访问保密信息,就会显示无法访问保密信息的页面,并提醒用户进行注册。



返回登录页面后,输入正确的用户名和密码,点击登录。



后台程序判断用户名和密码正确无误后,在session中设置已登录的标记,然后跳转到保密信息页面。



我们要保护的页面是admin/index.jsp,为此我们在web.xml进行如下配置。

<filter>
    <filter-name>SecurityFilter</filter-name>
    <filter-class>anni.SecurityFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SecurityFilter</filter-name>
    <url-pattern>/admin/*</url-pattern>
</filter-mapping>

       
定义SecurityFilter过滤器,让它过滤匹配/admin/*的所有请求,这就是说,对/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,而且也只有转换成对应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。

直接映射一个请求。

<servlet-mapping>
    <servlet-name>ContactServlet</servlet-name>
    <url-pattern>/contact.do</url-pattern>
</servlet-mapping>

                
像第 6.3 节 “使用servlet改写联系簿”中对servlet的映射,只有当请求是/contact.do的时候才会执行ContactServlet。/contact.do?id=1或/contact.do?method=list&id=1的请求也可以匹配到ContactServlet,这是因为根据http规范,请求的路径不包含问号以后的部分。

映射一个路径下的所有请求。

<servlet-mapping>
    <servlet-name>EncodingFilter</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

                   
像第 7.1 节 “批量设置请求编码”中这样使用星号(*)的形式,可以将某个路径下的所有请求都映射到EncodingFilter过滤器下,如果这个路径下还有子路径,那么子路径下的请求也会被EncodingFilter过滤。所以 /*这种写法就会过滤应用下所有的请求。

如果像第 7.2 节 “用filter控制用户访问权限”中那样把映射配置成/admin/*,就会只处理/admin/路径下的请求,不会处理根路径下的/index.jsp和/failure.jsp。

需要注意的是,这种写法必须以/开头,写成与绝对路径的形式,即便是映射所有请求也要写成/*,不能简化成*。

映射结尾相同的一类请求。

<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

                 
具体效果请参考07-03的例子,index.jsp中有四个链接,分别指向/a1.do, /a2.do, /xx/b1.do, /xx/yy/c1.do。
 


web.xml中的ControllerServlet会接收以.do结尾的请求,并使用forward将请求转发到/test.jsp。


点击/a1.do的情况。



点击/xx/yy/c1.do的情况。



这样做的一个好处是语义更清楚,只要看到以.do结尾的请求就知道肯定是交给ControllerServlet处理了,不管这个请求是在根路径还是子路径下,都会准确无误的找到对应的servlet。

缺点就是不同路径之间进行forward,jsp里就不能再使用相对路径了,所以我们在test.jsp中使用request.getContextPath()获得当前应用在服务器中的位置(例子中是/07-03)将相对路径都组装成绝对路径,这种用法在以后也会经常用到。

<%
    pageContext.setAttribute("ctx", request.getContextPath());
%>
<p><a href="${ctx}/index.jsp">返回</a></p>

                   
最后需要注意的是,这种请求映射就不能指定某一路径了,它必须是以星号(*)开始字母结尾,不能写成/*.do的形式。

现在咱们也发现java的请求映射有多傻了,灵活配置根本是不可能的任务。

想要获得所有以user开头.do结尾的请求吗?user*.do在url-pattern是无法识别的,只能配置成*.do,再去servlet中对请求进行筛选。

想要让一个servlet负责多个请求吗?/user/*,/admin/*,*.do写在一起url-pattern也不认识,只能配成多个servlet-mapping。

<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>/user/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>/admin/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ControllerServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>


java的复杂性在此处显露无疑。实际使用时,最好不要依赖web.xml中的配置,在自己的类中实现灵活配置才是正途。

7.3.2. 过滤链
其实在07-02这个例子里,我们使用了两个过滤器,EncodingFilter负责设置编码,SecurityFilter负责控制权限,那这两个过滤器是怎么起作用的呢?它们两个同时过滤一个请求时谁先谁后呢?

下面这个图会告诉我们答案。



所有的奥秘就在Filter中的FilterChain中。服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序就如上图所示,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilter()之后的代码,最后返回响应。

因此在07-02中执行的代码顺序是:

执行EncodingFilter.doFilter()中chain.doFilter()之前的部分:request.setCharacterEncoding("gb2312");

执行SecurityFilter.doFilter()中chain.doFilter()之前的部分:判断用户是否已登录。

如果用户已登录,则访问请求的资源:/admin/index.jsp。

如果用户未登录,则页面重定向到:/failure.jsp。

执行SecurityFilter.doFilter()中chain.doFilter()之后的部分:这里没有代码。

执行EncodingFilter.doFilter()中chain.doFilter()之后的部分:这里也没有代码。

过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时,就要特别注意过滤链的执行顺序问题,像EncodingFilter就一定要放在所有Filter之前,这样才能确保在使用请求中的数据前设置正确的编码。

7.4. filter的详细配置
我们已经了解了filter的基本用法,还有一些细节配置在特殊情况下起作用。

在servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求和<%@ include file="/index.jsp"%>的情况。

到了servlet-2.4中Filter默认下只拦截外部提交的请求,forward和include这些内部转发都不会被过滤,但是有时候我们需要forward的时候也用到Filter,这样就需要如下配置。

<filter>
    <filter-name>TestFilter</filtername>
    <filter-class>anni.TestFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>TestFilter</filtername>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>EXCEPTION</dispatcher>
</filter-mapping>

      
这样TestFilter就会过滤所有状态下的请求。如果我们没有进行设置,默认使用的就是REQUEST。而EXCEPTION是在isErrorPage="true"的情况下出现的,这个用处不多,看一下即可。

这里FORWARD是解决request.getDispatcher("index.jsp").forward(request, response);无法触发Filter的关键,配置上这个以后再进行forward的时候就可以触发过滤器了。

Filter还有一个有趣的用法,在filter-mapping中我们可以直接指定servlet-mapping,让过滤器只处理一个定义在web.xml中的servlet。

<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <servlet-name>TestServlet</servlet-name>
</filter-mapping>

<servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>anni.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/TestServlet</url-pattern>
</servlet-mapping>

     
直接指定servlet-name,TestFilter便会引用TestServlet配置的url-pattern,在某些filter与servlet绑定的情况下不失为一个好办法。

文章转载自:http://www.family168.com/tutorial/jsp/html/jsp-ch-07.html
分享到:
评论

相关推荐

    第22章 Filter过滤器.pdf

    【第22章 Filter过滤器】详解 在Java Web开发中,Filter过滤器是一个至关重要的组件,它允许开发者在客户端请求到达目标资源(如Servlet)之前进行预处理,并在响应返回给客户端之前进行后处理。这章我们将深入探讨...

    Java_Filter过滤机制详解.doc

    ### Java Filter过滤机制详解 #### 一、什么是Filter Filter技术是Servlet 2.3版本中新引入的一个功能,它的主要作用在于对Web应用中的请求和响应进行预处理和后处理。Filter本身并不是一个Servlet,因此它不能...

    jsp入门教程

    #### 第7章 使用Filter过滤请求 - **批量设置请求编码**:使用Filter统一设置所有请求的字符编码。 - **用Filter控制用户访问权限**:实现用户认证逻辑,控制不同用户对资源的访问权限。 - **Filter的特性**: - **...

    过滤器的使用.rar

    7. **第三方过滤器组件**:除了自定义过滤器,还有一些成熟的第三方组件,如Shiro、Spring Security,它们提供了更高级别的安全过滤器,简化了权限控制的实现。 通过"过滤器的使用.rar"中的JDBC三层项目,你可以...

    Java SpringBoot实现的过滤器(和拦截器)控制登录页面跳转

    本项目主要关注的是如何使用过滤器(Filter)和拦截器(Interceptor)来实现登录页面的控制与跳转。以下是对这些知识点的详细说明: 1. **SpringBoot**: SpringBoot是Spring框架的一个子项目,旨在简化Spring应用...

    Windows 文件系统过滤驱动开发教程(第二版)

    《Windows 文件系统过滤驱动开发教程(第二版)》是一本深度探讨Windows操作系统中Minifilter驱动程序开发的专业教程。在Windows系统中,文件系统过滤驱动是控制文件操作的关键技术,它允许开发者对文件系统的读写、...

    Servlet过滤器

    如果一个请求需要通过三个过滤器,那么第一个过滤器会先运行,接着是第二个,最后是第三个,响应时则逆序执行。 5. **应用场景** - **权限控制**:过滤器可以检查用户是否已登录,未登录的用户将被重定向到登录...

    JavaWeb程序设计习题参考答案(第7章).doc

    ### 第七章 监听和过滤 #### 1. 调用`ServletContext`的`getAttribute()`方法时,会触发哪个方法调用? 题目描述:当调用`ServletContext`接口的`getAttribute()`方法时,在存在关联监听器的情况下,会触发哪个...

    Javaweb第六章的代码

    8. **MVC(Model-View-Controller)模式**:第六章可能介绍了如何使用MVC模式来组织JavaWeb应用,这有助于提高代码的可维护性和可扩展性。模型层处理业务逻辑,视图层负责展示,控制器协调两者。 9. **过滤器和监听...

    android应用开发详解第八章

    这一章可能会涵盖如何创建Intent,如何使用Intent启动Activity以及如何设置Intent Filter来响应特定的Intent请求。 3. **视图和布局(Views and Layouts)**:Android的用户界面由各种View和Layout构建。第八章可能...

    shiro 第七章 完整Demo

    在第七章中,我们将重点讨论如何将 Shiro 集成到 Web 应用程序中,以实现全面的安全管理。 首先,Shiro 的集成过程通常涉及以下几个关键步骤: 1. **配置 Shiro Filter**: 在 Web 应用的 `web.xml` 文件中,我们...

    《Java Web程序设计任务教程》-教材源码第9章.rar

    7. 实战项目:除了理论知识,该教材的第9章源码还可能包含了一个或多个实战项目,这些项目将帮助你将所学应用到实际开发环境中,提升你的编程和调试技能。 通过分析和运行这些源码,你不仅能够加深对Java Web开发的...

    《Java Web程序设计任务教程》-教材源码第6章.rar

    6. **过滤器(Filter)和监听器(Listener)**:这两种组件在Java Web中用于拦截请求、响应或监控特定事件。过滤器可以用来做权限控制、字符编码转换等,而监听器可以监听session的创建和销毁、ServletContext的初始...

    Fiverr_BuyersRequest_Filter:TampermonkeyGreasemonkey脚本,使用指定的关键字过滤出Fiverr上的买方请求

    script_filter.js使用指定的关键字过滤出在Fiverr上的买方请求。 script_name.js修改“ Send Request按钮,并将其替换为买方的名称。 将两个脚本中的以下第7行替换为Fiverr用户名: // @match ...

    Java Web程序设计教程源码3-9章

    5. **第7章:Session和Cookie管理** Session和Cookie是Web应用中维持用户状态的两种常见机制。本章会详细介绍它们的工作原理,何时使用Session,何时使用Cookie,以及如何在Java Web应用中实现它们。 6. **第8章:...

    第9章 Servlet高级_课后题_gh210628.docx

    第 9 章 Servlet 高级知识点总结 一、Filter 接口 * Filter 中包含了 3 个接口,分别是 Filter 接口、FilterConfig 接口和 FilterChain 接口。 * Filter 被称为过滤器,其基本功能是对 Servlet 容器调用 Servlet ...

    30天学通java web项目案例开发(第五章)源码

    6. **过滤器(Filter)和监听器(Listener)**:这两者是Java Web的重要组件,可以用来拦截请求、响应,或者监听特定的事件。学习者可能会学习如何编写和配置这两个组件。 7. **JNDI(Java Naming and Directory ...

    《Java Web程序设计任务教程》-教材源码第10章.rar

    6. **过滤器(Filter)**:过滤器允许我们在请求处理前后执行额外的任务,如认证、字符编码转换等。章节可能包含如何定义和配置Filter,以及它们在Web应用中的作用。 7. **部署描述符(Deployment Descriptor, web....

    V512工作室Java web的ppt

    第7章 讲解Servlet会话(Session)编程的相关内容。 第8章 讲解JSP技术、JSP内置对象以及JSP页面之间、JSP与Servlet之间的相互转向。 第9章 讲解JavaBeans技术的概念和JavaBeans的使用。 第10章 讲解如何运用MVC模式...

Global site tag (gtag.js) - Google Analytics