1、第第9章章 过滤器和监听器技术过滤器和监听器技术 过滤器(过滤器(Filter)和监听器()和监听器(Listener)是两种特殊的)是两种特殊的Servlet技术。过滤器可以对用户的请求信息和响应信息技术。过滤器可以对用户的请求信息和响应信息进行过滤,常被用于权限检查和参数编码统一设置等。进行过滤,常被用于权限检查和参数编码统一设置等。监听器可以用来对监听器可以用来对Web应用进行监听和控制的,增强应用进行监听和控制的,增强Web应用的事件处理能力。应用的事件处理能力。本章主要介绍过滤器和监听器的编程接口、基本结本章主要介绍过滤器和监听器的编程接口、基本结构、信息配置、部署和运行,最后通过案
2、例说明过滤器构、信息配置、部署和运行,最后通过案例说明过滤器和监听器的典型应用。和监听器的典型应用。第9章 过滤器和监听器技术9.1 过滤器技术9.2 监听器技术本章小结9.1 过滤器技术 过滤器是在服务器上运行的,且位于请求与响应中间的起过滤功能的程序,其工作原理如图9-1所示。9.1 过滤器技术 在与过滤器相关联的Servlet或JSP运行前,过滤器先执行。一个过滤器可以与一个或多个Servlet或JSP绑定,可以检查访问这些资源的请求信息。检查请求信息后,过滤器可以选择下一个动作:正常调用请求的资源(即Servlet或JSP).用修改后的请求信息调用请求资源。调用请求的资源,修改请求响应
3、,再将响应发送到客户端。禁止调用该资源,将请求重定向到其它的资源,或返回一个特定的状态码,或产生替换的输出。9.1 过滤器技术9.1.1 过滤器编程接口9.1.2 设计过滤器9.1.3 案例基于过滤器的用户权限控制9.1.4 案例基于过滤器的中文乱码解决9.1.5 案例禁止未授权的IP访问站点过滤器本节主要内容:9.1.1 过滤器编程接口 进行过滤器编程用到javax.servlet.jar中的一组接口和类,表9-1只列出了与过滤器设计有关的三个重要接口,而与Servlet编程有关的接口、类请参考第6章。表9-1 Servlet编程接口功能类和接口Filter实现javax.servlet.F
4、ilterFilter配置javax.servlet.FilterConfigFilter链javax.servlet.FilterChain接口Filter的主要方法:(1)init()方法方法原型:public void init(FilterConfig filterConfig)throws ServletException 该方法用于初始化过滤器,并获取web.xml文件中配置的过滤器初始化参数,默认情况下,服务器启动时就会加载过滤器,init方法就会执行。该方法有一个FilterConfig类型的参数,利用它可以获取在web.xml中设置的过滤器的初始化参数值。获取初始参数值的方法
5、为:public String getInitParameter(String paraName)过滤器的信息配置将在9.1.3节中介绍。接口Filter的主要方法:(2)doFilter()方法 方法原型:public void doFilter(ServletRequest request,ServletResponse response,FilterChain filterChain)throws IOException,ServletException 当请求地址和过滤地址匹配时将进行过滤操作,该方法被执行。第1个参数为ServletRequest对象,此对象给过滤器提供了对请求信息(
6、包括表单数据、Cookie和HTTP请求头)的完全访问。第2个参数为ServletResponse,用于响应请求。第3个参数为FilterChain对象,使用该参数对象调用Servlet、JSP页面或者过滤器链中的下一个过滤器。调用方法为:public void doFilter(ServletRequest request,ServletResponse response)接口Filter的主要方法:(3)destroy()方法 方法原型:public void destroy()Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源。性质等同于Se
7、rvlet的destory()方法。这些方法构成了过滤器对象的生命周期:创建、执行过滤方法、销毁。9.1.2 设计过滤器 过滤器的设计需要实现Filter接口,并要根据处理的功能需要,实现Filter接口中的3个方法:1、public void init(FilterConfig filterConfig)throws ServletException2、public void doFilter(ServletRequest request,ServletResponse response,FilterChain filterChain)throws IOException,ServletEx
8、ception3、public void destroy()9.1.2 设计过滤器package;import;public class Filter1 implements Filter/这里是给出Filter的一个实现类Filter1public void destroy()/添加代码 public void doFilter(ServletRequest request,ServletResponse response,FilterChain filterChain)throws IOException,ServletException /添加代码 public void init(Fi
9、lterConfig filterConfig)throws ServletException/添加代码1.过滤器基本结构 一个过滤器程序的基本结构如下:9.1.2 设计过滤器 创建实现Filter接口的类:在项目的src下,创建一个或多个过滤器,并采用“包”结构的方式组织所有的过滤器。实现init方法,读取过滤器的初始化函数。将过滤行为放入doFilter()方法中:实现doFilter(),完成该过滤器所需要过滤功能。调用filterchain对象的doFilter方法:filterChain对象是过滤器接口的doFilter方法的一个参数,调用Filterchain的doFilter方法
10、时,下一个关联的过滤器将被调用,若没有其他与Servlet或JSP相关联的过滤器,就调用Servlet或JSP本身。将过滤器与特定的Servlet或JSP页面关联:使用部署配置文件(web.xml)中的filter元素和filter-mapping元素。2过滤器的建立在MyEclipse开发环境下创建过滤器是很方便的,其创建过程:第1步:实现Filter的接口,建立过滤器 选中工程,右击工程src目录,选择“new”,再选择“class”,显示如图9-2所示的对话框,并按提示输入有关的信息:在包filter下新建过滤器类Filter_first.java。点击图9-2中的“Finish”按钮后
11、,就建立了过滤器程序的基本框架。新建的Servlet的基本结构代码:第2步:将过滤行为放入doFilter()方法中,按功能需要,实现doFilter()方法。第3步:过滤器与相关联的servlet、JSP注册,过滤器的配置信息要在web.xml注册。配置过滤器需要使用和元素,并且要放在与之间。其配置格式:FilterName package.className ParamName1 ParamValue1 FilterName/pathFilterName ServletName说明:(1)标签(可有多个)用来设置过滤器的初始化参数,在init(FilterConfig config)方法中
12、通过config的getInitParameter(ParamName)获得参数值。(2)url-pattern用来设定过滤器的过滤地址,可带通配符“/*”,表示任何地址都要经过该过滤器。(3)若对Servlet实现过滤,其配置方式是:FilterName ServletName9.1.2 设计过滤器3过滤器的部署与运行 过滤器编译后的字节码文件必须部署到web目录/WEB-INF/classes下才能起作用。9.1.3 案例基于过滤器的用户权限控制 【例9-1】在一个Web应用程序中,有些JSP页面或Servlet必须是注册用户登录后才有权访问。设计一个过滤器用于对用户是否是登录用户进行检验
13、。【分析】判断一个用户是否登录的方法通常是:当用户登录成功后,将用户名存放到session范围内(session.setAttribute(“u_name”,username)),判断时,从session中取出u_name属性(session.getAttribute(“u_name”),若取值不为空就是登录用户,否则,就不是登录用户,可转入注册页面。在每个需要登录用户才可以访问的页面或Servlet中加入登录检验代码很冗余,可以通过编写过滤器统一解决,过滤地址设为需要进行登录检验的那些Servlet或JSP的地址。9.1.3 案例基于过滤器的用户权限控制【设计关键】(1)在过滤器中获取ses
14、sion对象(HttpSession):HttpServletRequest requ=(HttpServletRequest)request;HttpSession session=requ.getSession(true);(2)过滤地址在web.xml中设置,假设是以/admin打头的所有地址。loginfilter filters.LoginFilter loginfilter/admin/*要设计的组件:(1)登录检验过滤器:LoginFilter.java(2)在Web.xml中注册配置过滤器(3)在WebRoot目录下,创建页面(register.jsp)、以及登录处理页面(主要
15、将请求参数uaerName保存到session中的u_name属性中。(4)在WebRoot自下创建目录admin,并创建登录页面(login.jsp)【实现】(1)登录检验过滤器:LoginFilter.java(2)在Web.xml中注册配置过滤器(3)在WebRoot目录下,创建页面:登录页面(login.jsp)登录成功页面(finish.jsp)(4)在WebRoot自目录下,创建目录admin,并在该目录下创建页面:注册页面(register.jsp)登录处理页面saveSession.jsp(主要将请求参数uaerName保存到session中的u_name属性中)。【运行】执行
16、程序1 执行程序2【提示】每次运行注意网址的变化。9.1.4 案例基于过滤器的中文乱码解决【例9-2】设计一个过滤器统一处理Post提交方式下参数值中文乱码问题。【分析】对于有汉字信息处理的Servlet或JSP,可以通过编写过滤器实现请求和请求响应的统一汉字编码。过滤地址设为需要进行编码转换的那些Servlet或JSP的地址。【设计关键】解决post提交方式下参数值中文乱码:request.setCharacterEncoding(UTF-8);【实现】(1)编码转换过滤器:代码如:EncodingFilter.java(2)过滤地址在web.xml中设置,假设是以/servlet打头的所有
17、地址。9.1.5 案例禁止未授权的IP访问站点过滤器【例9-3】设计一个过滤器禁止未授权的IP访问站点。【分析】使用过滤器禁止未授权的IP访问站点是过滤器常见的应用之一。其业务流程是:将需要禁止的IP地址在web.xml中配置,利用init()方法读取该IP地址,并在dofilter()中使用。【设计关键】在init()获取web.xml中禁止的IP地址,需要将该值保存到过滤器的属性中,供doFilter()使用。(1)需要设置一个私有属性FilteredIP,用于存放被过滤的IP值。(2)在web.xml中配置IP信息:代码如:例9-3(配置IP).txt(3)在init()方法中,利用ge
18、tInitParameter()从web.xml中获取IP地址信息。(4)在doFilter()方法中,实现IP地址的过滤。当是被禁止的IP时,转入禁止提示页面ErrorInfo.jsp,否则,正常执行。9.1.5 案例禁止未授权的IP访问站点过滤器【实现】(1)创建一个过滤器操作类在src目录下,建立一个IP过滤操作FilterIP类,包名为test,文件名为FilterIP.java,代码如下 例9-3(1)(2)修改web.xml文件,在web.xml文件中添加以下配置代码:9-3(2)(3)新建一个Succeed.jsp文件,其代码如下 例9-3(3)(4)新建一个ErrorInfo.
19、jsp文件,代码如下 例9-3(4)9.2 监听器技术 监听器是Web应用开发的一个重要组成部分。通过它可以监听Web应用的上下文信息、Servlet请求信息、Servlet会话信息,并自动根据不同情况,在后台调用相应的处理程序。利用监听器来对Web应用进行监听和控制的,极大地增强了Web应用的事件处理能力。监听器运行机制:当服务器启动时,监听器自动加载(执行构造函数),特定事件发生时,容器自动调用相应监听器中对应的事件处理方法。9.2 监听器技术 9.2.1 监听器编程接口9.2.2 设计监听器9.2.3 案例会话计数监听器的设计本节主要内容:9.2.1 监听器编程接口 监听器编程要用到ja
20、vax.servlet.jar中的一组监听接口和事件类。根据监听对象的不同,监听器划分为3种:(1)ServletContext事件监听器:用于监听应用程序环境对象。(2)HttpSession事件监听器:用于监听用户会话对象。(3)ServletRequest事件监听器:用于监听请求消息对象。9.2.1 监听器编程接口 这3种监听器共包含了8个监听接口,6个监听事件类,如表9-2所示。监听对象监听接口监听事件ServletRequestServletRequestListenerServletRequestEventServletRequestAttributeListenerServlet
21、RequestAttributeEventHttpSessionHttpSessionListenerHttpSessionEventHttpSessionActivationListenerHttpSessionAttributeListenerHttpSessionBindingEventHttpSessionBindingListenerServletContextServletContextListenerServletContextEventServletContextAttributeListenerServletContextAttributeEvent 1.监听监听Servle
22、tContext对象对象 对ServletContext对象实现监听,可以监听到ServletContext对象中属性的变化(增加、删除、修改操作),也可以监听到ServletContext对象本身的变化(创建与销毁)。常用的监听方法如表9-3所示。接口名称接口方法激发条件ServletContextAttributeListenervoid attributeAdded(ServletContextAttributeEvent scab)增加属性void attributeRemoved(ServletContextAttributeEvent scab)删除属性void attribute
23、Replaced(ServletContextAttributeEvent scab)修改属性ServletContext.Listenervoid contextInitialized(ServletContextAttributeEvent scab)创建对象void contextDestroyed(ServletContextAttributeEvent scab)销毁对象 2.监听会话监听会话常用监听方法如表9-4所示。接口名称接口方法激发条件HttpSessionAttributeListenervoid attributeAdded(HttpSessionBindingEvent
24、 hsbe)增加属性void attributeRemoved(HttpSessionBindingEvent hsbe)删除属性void attributeReplaced(HttpSessionBindingEvent hsbe)修改属性HttpSessionListenervoid sessionCreated(HttpSessionEvent hse)创建对象void sessionDestroyed(HttpSessionEvent hse)销毁对象HttpSessionActivationListenervoid sessionDidActivate(HttpSessionEven
25、t hse)会话刚被激活void sessionWillPssivate(HttpSessionEvent hse)会话将要钝化HttpSessionBindingListenervoid valueBound(HttpSessionBindingEvent hsbe)调用setAttribute()void valueBound(HttpSessionBindingEvent hsbe)调用removeAttribute()3.监听请求监听请求常用监听方法如表9-5所示。接口名称接口方法激发条件ServletContextAttributeListenervoid attributeAdde
26、d(ServletRequestAttributeEvent srae)增加属性void attributeRemoved(ServletRequestAttributeEvent srae)删除属性void attributeReplaced(ServletRequestAttributeEvent srae)修改属性ServletRequestListenervoid requestInitialized(ServletRequestEvent sre)创建对象void requestDestroyed(ServletRequestEvent sre)销毁对象9.2.2 设计监听器 (1)
27、实现合适的接口:监听器需要根据监听对象的不同,实现表9-2中的某个监听接口。(2)实现有关事件的方法:按所选择的监听器接口,实现该接口中的有关的方法。(3)获取对重要Web应用对象的访问:在事件处理方法中,可能会用到9个重要对象(分为3类):servlet上下文、变化后的servlet上下文属性的名称、变化后的servlet上下文属性的值。会话对象、变化后的会话属性的名称、变化后的会话属性的值。请求对象、变化后的请求对象属性的名称、变化后的请求对象属性的值。设计一个监听器一般需要以下步骤:9.2.2 设计监听器 (4)使用这些对象:需要根据具体应用,选择有关的对象。例如,对于servlet上下
28、文,可能会读取初始化参数(getInitParameter方法),存储数据供以后使用(setAttribute方法)和读取原先存储的数据(getAttribute方法)。(5)配置监听器:在web.xml中,利用listener元素和listener-class元素完成配置。(6)提供任何需要的初始化参数:servlet上下文监听器一般先读取servlet上下文的初始参数,并将这些参数作为所有servlet或JSP都可以使用的数据基础。在web.xml中使用context-param元素提供这些初始化参数的名称和值。设计一个监听器一般需要以下步骤:9.2.2 设计监听器 一个监听器程序的基本结
29、构如下:package;import ;public class 监听器实现类名 implements*Listerner实现*Listerner中的方法1监听器基本结构:监听器基本结构:9.2.2 设计监听器 监听器的配置信息同样写在web.xml里,配置相对简单,不需要配置地址:package.className2 监听器信息配置监听器信息配置 3监听器的部署与运行监听器的部署与运行监听器编译后的字节码文件同样部署到web目录/WEB-INF/classes下。9.2.3 案例会话计数监听器的设计 【例例9-4】使用监听器统计与显示在线用户数目。使用监听器统计与显示在线用户数目。【分析】在
30、网站中经常需要进行在线人数的统计。过去的一般做法是结合登录和退出功能,即当用户登录的时候计数器加1,当用户点击退出按钮时计数器减1。这种处理方式存在两个缺点:一是用户正常登录后,可能会忘记点击退出按钮,而直接关闭浏览器,导致计数器减1的操作不会执行;二是该方法无法统计非登录的在线人数。9.2.3 案例会话计数监听器的设计 【分析2】可以利用监听器来解决这个问题,实现更准确的在线人数统计功能。当一个浏览器第一次访问网站的时候,服务器会新建一个HttpSession对象,并触发HttpSession创建事件,如果注册了HttpSessionListener事件监听器,则会调用HttpSession
31、Listener事件监听器的sessionCreated方法。相反,当这个浏览器用户注销或访问结束超时的时候,服务器会销毁相应的HttpSession对象,触发HttpSession销毁事件,同时调用所注册HttpSessionListener事件监听器的sessionDestroyed方法。这样,我们只需要在HttpSessionListener实现类的sessionCreated方法中让计数器加1,在sessionDestroyed方法中让计数器减1,就可实现网站在线人数的统计功能。9.2.3 案例会话计数监听器的设计 【设计关键设计关键】选择正确的监听接口并实现相应抽象方法:由上面分析知
32、道,要监听HttpSession对象的创建和销毁,因此监听器类要实现的接口为HttpSessionListener。【实现实现】(1)设计监听器类:OnlineListener.java(2)修改web.xml文件,添加以下配置代码:listener.OnlineListener(3)显示在线人数的页面online.jsp代码如下:在线人数显示页面 当前的在线人数:本章小结 本章介绍了过滤器和监听器。过滤器主要用来拦截用户请求,实现如权限检查、编码转换、加密等通用的“横向”模块;监听器主要用来监听Web应用的上下文信息、Servlet请求信息、Servlet会话信息,并根据不同的情况,在后台调用相应处理程序。给出了过滤器和监听器的设计方法及其应用案例。上机实习1编写过滤器实现:只允许客户端IP地址是219.218.*.*形式的访问站点,否则转到Error页面。2编写监听器监听请求对象的创建和销毁。