JavaWeb之会话跟踪-过滤器和监听器
会话跟踪
在面前的章节中可以知道,Java Web正常工作的基础就是Http协议,但是Http协议是无状态的,也就是说该协议对于是哪个用户访问了服务器并不知晓,如果只是简单的访问页面不需要服务器知道用户是谁这种情况,HTTP协议完全可以满足需要,但是对于服务器需要知道访问者是谁的情况显然HTTP协议无法满足需要,例如,在购物网站购买商品,通常情况下服务器需要知道是谁购买了什么商品等信息。
会话跟踪的技术
鉴于HTTP协议无状态的属性,服务器并不知道用户有没有登录,登录的用户是谁,这就需要会话跟踪,换句话说就是要想办法让服务器知道是谁在访问服务器,或者在服务端保存一份访问者的信息。在本小节内容中,将详细了解会话跟踪的技术。
会话从浏览器访问某个页面开始,到浏览器关闭这段时间叫做会话。在会话当中可以发出任意次数的请求,会话指的是一段时间或者一个过程,例如A和B打电话的过程就是一次会话,当电话接通时此时会话开始,A、B双方交流完毕以后挂断电话,则会话结束,在会话开始到结束的过程中A和B可以说无数句话。
会话跟踪的技术通常由如下4种,其中第3种需要在页面内放置一个隐藏表单,表单内放置着能鉴别用户的信息。这么做可能会带来安全隐患,因此第3种方式并不常用。
- Cookie
- Session
- 隐藏表单域
- URL重写
Cookie及常用方法
Cookie是一小段文本,保存在客户端,随着请求与响应在客户端和浏览器之间来回传递。当向一台服务器发出请求并得到响应后,用浏览器打开开发者工具在Network选项卡中查看请求报文和响应报文,就会发现在请求头和响应头中都存在Cookie,如下图所示:
方法 | 描述 |
---|---|
public void setMaxAge(int expiry) | 设置Cookie的最大存活时间,单位为秒,如果expiry是正值,则代表expiry秒后cookie过期,如果是0,则响应到客户端浏览器后立刻删除,如果是负值,则表示浏览器关闭时即cookie过期。 |
public void setPath(String uri) | 设置Cookie的路径,只有在相同路径下才能访问到对应的Cookie |
public void setDomain(String domain) | 设置Cookie所属的域,以“.”开头 |
public void setHttpOnly(boolean isHttpOnly) | 设置脚本是否可以访问cookie,值为false时脚本可访问,值为true时,脚步不可访问 |
Session及常用方法
JavaEE中使用Session对象表示会话,通过Session中保存用户名可以让服务器记住用户的访问和判断用户是否登录,Session常用方法如下:
方法 | 描述 |
---|---|
public long getCreationTime(); | 获取创建Session的时间 |
public String getId(); | 获取Session的唯一标识 |
public boolean isNew(); | 判断当前会话是否是新的会话 |
public long getLastAccessedTime(); | 获取最后一次请求的时间 |
public void setMaxInactiveInterval(int interval); | 指定在servlet容器使会话失效之前客户端请求之间的时间单位为秒 |
public Object getAttribute(String name); | 获取session中封装的数据 |
public void setAttribute(String name, Object value); | session中封装数据 |
public void removeAttribute(String name); | 移除session中封装的数据 |
public void invalidate(); | 强制session过期 |
设置Session过期也可以在Tomcat中conf目录下web.xml中修改配置
1 | <session-config> |
会话跟踪实战
在了解了Session的常用方法以后就可以使用Session进行会话跟踪,也就是将用户的登录信息保存进Session中,在整个会话过程中服务器都可以获取到是哪个用户在访问服务器。
1 | package cn.bytecollege.controller; |
上面的代码中模拟了登录的过程,当用户名和密码正确时,将用户名保存进session,当用户再次访问时就可以从会话中获取该用户名,一方面可以判断用户是否登录,另一方面服务器即可知道是哪个用户访问了服务器。
Session和Cookie的区别
- Session保存在服务端,而Cookie保存在客户端,因此Cookie安全性低于session的
- Session可以保存对象,而Cookie只能保存字符串
- Session是JSP内置对象,Cookie不是内置对象
Session的活化和钝化
- 当客户端访问服务器,服务器创建好会话,此时如果关闭服务器,服务器会将当前Session持久化到work目录下,扩展名为.ser的文件。这个过程就叫Session钝化,需要注意的是保存在Session中的对象一定要实现序列化接口。
- 当服务器再次启动时,web容器会读取.ser文件。并恢复该Session对象。这个过程就叫Session的活化
过滤器和监听器
过滤器概述
过滤器就是在访问目标资源前起过滤作用的中间组件。例如,污水净化设备可以看做现实中的一个过滤器,它负责将污水中的杂质过滤,从而使进入的污水变成净水。而对于Web应用程序来说,过滤器是一个和Servlet同等地位的Web组件,它可以截取客户端和资源之间的请求与响应信息,并对这些信息进行过滤,并且在此过程中目标资源并不会感知到过滤器的存在。
当Web容器接收到一个对资源的请求时,它将判断是否有过滤器与这个资源相关联。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时,容器同样会将响应先转发给过滤器,在过滤器中,你可以对响应的内容进行转换,然后再将响应发送到客户端。从上述过程可以看出,客户端和目标资源并不需要知道过滤器的存在,在一个Web应用程序中,可以部署多个过滤器,这些过滤器组成了一个过滤器链。过滤器链中的每个过滤器负责特定的操作和任务,客户端的请求在这些过滤器之间传递,直到目标资源。
在请求资源时,过滤器链中的过滤器将依次对请求进行处理,并将请求传递给下一个过滤器,直到目标资源;在发送响应时,则按照相反的顺序对响应进行处理,直到客户端。过滤器并不是必须要将请求传送到下一个过滤器(或目标资源),它也可以自行对请求进行处理,然后发送响应给客户端,或者将请求转发给另一个目标资源。
下面是过滤器在Web开发中的一些主要应用:
- 对用户请求进行统一认证。
- 对用户的访问请求进行记录和审核。
- 对用户发送的数据进行过滤或替换。
- 对响应内容进行压缩,减少传输量。
- 对请求和响应进行加解密处理。
Filter API
Filter接口
与开发Servlet要实现javax.servlet.Servlet接口类似,开发过滤器要实现javax.servlet.Filter接口,并提供一个公开的不带参数的构造方法。在Filter接口中,定义了下面的3个方法。
1 | public void init(FilterConfig filterConfig) throws ServletExceptionWeb |
容器调用该方法来初始化过滤器。容器在调用该方法时,向过滤器传递FilterConfig对象,FilterConfig的用法和ServletConfig类似。利用FilterConfig对象可以得到ServletContext对象,以及在部署描述符中配置的过滤器的初始化参数。在这个方法中,可以抛出ServletException异常,通知容器该过滤器不能正常工作。
1 | public void doFilter(ServletRequest request, ServletResponseresponse, FilterChain chain) throws java.io.IOException,ServletExceptiondoFilter() |
方法类似于Servlet接口的Service()方法。当客户端请求目标资源的时候,容器就会调用与这个目标资源相关联的过滤器的doFilter()方法。在这个方法中,可以对请求和响应进行处理,实现过滤器的特定功能。在特定的操作完成后,可以调用chain.doFilter(request, response)将请求传给下一个过滤器(或目标资源),也可以直接向客户端返回响应信息,或者利用RequestDispatcher的forward()方法,以及HttpServletResponse的sendRedirect()方法将请求转向到其他资源。需要注意的是,这个方法的请求和响应参数的类型是ServletRequest和ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议。
1 | public void destroy() |
Web容器调用该方法指示过滤器的生命周期结束。在这个方法中,可以释放过滤器使用的资源。
FilterConfig接口
javax.servlet.FilterConfig接口类似于javax.servlet.ServletConfig接口,用于在过滤器初始化时向其传递信息。FilterConfig接口由容器实现,容器将其实例作为参数传入过滤器对象的init()方法中。在FilterConfig接口中,定义了以下4个方法。
1 | public java.lang.String getFilterName() |
得到在部署描述符中指定的过滤器的名字。
1 | public java.lang.String getInitParameter(java.lang.String name) |
返回在部署描述中指定的名字为name的初始化参数的值。如果这个参数不存在,该方法将返回null。
1 | public java.util.Enumeration getInitParameterNames() |
返回过滤器的所有初始化参数的名字的枚举集合。如果过滤器没有初始化参数,这个方法将返回一个空的枚举集合。
1 | public ServletContext getServletContext() |
返回Servlet上下文对象的引用。
FilterChain接口
javax.servlet.FilterChain接口由容器实现,容器将其实例作为参数传入过滤器对象的doFilter()方法中。过滤器对象使用FilterChain对象调用过滤器链中的下一个过滤器,如果该过滤器是链中最后一个过滤器,那么将调用目标资源。FilterChain接口只有一个方法,如下:
1 | public void doFilter(ServletRequest request, ServletResponseresponse) throws java.io.IOException, ServletException |
调用该方法将使过滤器链中的下一个过滤器被调用。如果调用该方法的过滤器是链中最后一个过滤器,那么目标资源被调用。
过滤器部署
web.xml注册
1 |
|
注解方式
1 | package cn.bytecollege.filter; |
过滤器实战
在第2章的学习中可以了解到在web开发中会出现请求乱码的问题,通常解决办法是在接收数据前,使用request的方法,手动设置字符编码request.setCharachterEncoding("utf-8");
,这种方式虽然简洁方便,但是在所有的Servlet中都需要设置,因此急需要一个一劳永逸的方法。过滤器就可以帮助开发者实现这种效果。当客户端发送请求后,如果有与之对应的过滤器存在,则会先经过过滤器。那么在请求到达Servlet之前先对字符编码进行转换即可。下面将示范一个完整的字符编码过滤器。
1 | package cn.bytecollege.filter; |
在上述代码中首先获取了请求方式,如果是post请求则直接设置请求编码,如果是get请求,则先自定义HttpServletRequestWrapper的子类(该类就是HttpServletRequest的实现类),并重写getParamter()方法,在该方法内首先获取请求参数,然后将请求参数进行转码,并返回转码后的内容,实现如下:
1 | package cn.bytecollege.request; |
监听器
监听器的作用是监听Web容器的有效期事件,由容器管理。利用Listener接口监听在容器中的某个执行程序,并且根据其应用程序的需求做出适当响应。Java web提供了8个监听器,分别用于监听application、Session、request对象的创建及事件,这8个监听器如下:
接口 | 描述 |
---|---|
ServletContextListener | 用于监听ServletContext对象(也可以理解为application对象)的创建和销毁 |
ServletContextAttributeListener | 用于监听ServletContext对象属性的增加、删除和修改 |
HttpSessionListener | 用于监听Session对象的创建和销毁 |
HttpSessionActivationListener | 监听Session的活化与钝化 |
HttpSessionBindingListener | 用于监听Session中对象的绑定信息 |
HttpSessionAttributeListener | 用于监听Session中属性的绑定 |
ServletRequestListener | 用于监听ServletRequest对象的创建于销毁 |
ServletRequestAttributeListener | 用于监听request对象属性的增加、删除和修改 |
ServletContextListener接口
主要监听ServletContext的创建和删除。它提供了如下两个方法:
- contextInitialized(ServletContextEvent event)方法:通知正在收听的对象应用程序已经被加载及初始化。
- contextDestroyed(ServletContextEvent event)方法:通知正在收听的对象应用程序已经被载出,即关闭。
ServletContextAttributeListener接口
主要监听ServletContext属性的增加、删除及修改,它提供了如下3个方法。
- attributeAdded(ServletContextAttributeEvent event)方法:若有对象加入Application的范围,通知正在收听的对象。
- attributeReplaced(ServletContextAttributeEvent event)方法:若在Application范围内的一个对象取代另一个对象,通知正在收听的对象。
- attributeRemoved(ServletContextAttributeEvent event)方法:若有对象从Application的范围移除,通知正在收听的对象。
HttpSessionListener接口
该接口监听Session的创建及销毁,它提供了如下两个方法。
- sessionCreated(HttpSessionEvent event)方法:通知正在收听的对象,session已经被加载及初始化。
- sessionDestroyed(HttpSessionEvent event)方法:通知正在收听的对象,session已经被载出(HttpSessionEvent类的主要方法是getSession(),可以使用该方法回传一个session对象)。
HttpSessionActivationListener接口
该接口实现监听Session的活化或者钝化情况,它提供了如下3个方法。
- sessionDidActivate(HttpSessionEvent event)方法:通知正在收听的对象,其session已经变为有效状态。
- sessionWillPassivate(HttpSessionEvent event)方法:通知正在收听的对象,其session已经变为无效状态。
HttpSessionBindingListener接口
该接口实现监听Session对象的绑定信息,它是唯一不需要在web.xml中设置Listener的,并提供了以下两个方法。
- valueBound(HttpSessionBindingEvent event)方法:当有对象加入session的范围时,会被自动调用。
- valueUnBound(HttpSessionBindingEvent event)方法:当有对象从session的范围内移除时,会被自动取消调用。
HttpSessionAttributeListener接口
该接口实现监听Session中属性的设置请求,它提供了如下两个方法。
- attributeAdded(HttpSessionBindingEvent event)方法:若有对象加入session的范围,通知正在收听的对象。
- attributeReplaced(HttpSessionBindingEvent event)方法:若在session范围内的一个对象取代另一个对象,通知正在收听的对象。
- attributeRemoved(HttpSessionBindingEvent event)方法:若有对象从session的范围移除,通知正在收听的对象(HttpSessionBindingEvent类主要有3个方法,即getName()、getSession()和getValues())。
ServletRequestListener接口
该接口提供了如下两个方法。
- requestInitalized(ServletRequestEvent event)方法:通知正在收听的对象,ServletRequest已经被加载及初始化。
- requestDestroyed(ServletRequestEvent event)方法:通知正在收听的对象,ServletRequest已经被载出,即关闭。
ServletRequestAttributeListener接口
该接口提供了如下3个方法。
- attributeAdded (ServletRequestAttributeEvent event)方法:若有对象加入request的范围,通知正在收听的对象。
- attributeReplaced(ServletRequestAttributeEvent event)方法:若在request范围内的一个对象取代另一个对象,通知正在收听的对象。
- attributeRemoved(S