一、 Introduction
Web的本质是两端数据的交互,每一次数据交互的过程就像一次会话过程,交流过程。对于会话过程中产生的某些信息的记录,便于促进某些会话结果或者会话期间产生的业务的实现。在web中,数据库、request、servletContext都可以用来记录信息。数据库的存储属于持久化存储,比如,当用户进行网购时,需要根据用户登录的状态,来判断是否可以进入购物车操作,显然,使用数据库存储登录记录,就丧失了对于用户端状态的追踪,不具有实时性;而用request记录用户登录状态,由于request中的记录只在本次请求响应中有效,如果用户登录后,将这个登录信息存入request,当用户发起新的请求后,这个登录信息将失效,同样不能实现对用户端会话的追踪;如果采用servletContext来存储,由于servletContext为项目级别的存储容器,那么当用户登录状态改变时,则需要频频改变servletContext的信息,如果用户量庞大,将会导致低效和系统安全问题。Cookie和session,正是为了解决对于web中两端会话的追踪记录,而被使用的web技术。
二、 Cookie
作为一种会话跟踪技术,cookie的产生早于session,当以个用户请求连接到每个服务器,服务器就会给这个用户一个标识,这个标识就是cookie。当大量的用户访问服务器时,服务器可以通过cookie区分开用户。Cookie中还记录了很多其他信息。在联网的条件下,打开某个网页,在地址栏输入JavaScript:alert(document. cookie),就可以查看服务器端为用户设定的cookie。Cookie的实现,需要浏览器的支持,如果浏览器不支持cookie或者禁用cookie,那么cookie将失效。作为服务器端,可以通过servlet操作cookie。Java提供了Cookie类专门用于对cookie的设置。由于cookie的内容,是服务器提供提供的,那么,cookie的内容要在首次响应后传给客户端。
1. JavaEE API
javax.servlet.http
Class Cookie
javax.servlet.http.Cookie
All Implemented Interfaces:
public class Cookie
extends Object
implements Cloneable
Implements: Cloneable
创建一个 cookie,cookie是 servlet 发送到 Web 浏览器的少量信息,这些信息由浏览器保存,然后发送回服务器。cookie 的值可以唯一地标识客户端,因此 cookie 常用于会话管理。
一个 cookie 拥有一个名称、一个值和一些可选属性,比如注释、路径和域限定符、最大生存时间和版本号。一些 Web 浏览器在处理可选属性方面存在 bug,因此有节制地使用这些属性可提高 servlet 的互操作性。
servlet 通过使用 HttpServletResponse#addCookie方法将 cookie 发送到浏览器,该方法将字段添加到HTTP 响应头,以便一次一个地将 cookie 发送到浏览器。浏览器应该支持每台 Web 服务器有 20 个cookie,总共有 300 个 cookie,并且可能将每个 cookie 的大小限定为 4 KB。
浏览器通过向 HTTP 请求头添加字段将 cookie 返回给 servlet。可使用 HttpServletRequest#getCookies方法从请求中获取 cookie。一些 cookie 可能有相同的名称,但却有不同的路径属性。
cookie 影响使用它们的 Web 页面的缓存。HTTP 1.0 不会缓存那些使用通过此类创建的 cookie 的页面。此类不支持 HTTP 1.1 中定义的缓存控件。
此类支持版本 0(遵守 Netscape 协议)和版本 1(遵守 RFC 2109 协议)cookie规范。默认情况下,cookie 是使用版本 0 创建的,以确保最佳互操作性。
2. 创建cookie
Cookie由服务器在服务器响应成功后产生,所以,cookie通过response对象传送到客户端。当第一次响应给客户端时,客户端程序,即浏览器中会保存这个cookie,同时,在浏览器的响应头中生成cookie信息,但是由于这是第一次请求发出时,所以,请求头中并没有cookie信息。而,当第一次之后的请求时,如果没有删除cookie,如果没有关闭页面或浏览器,那么,请求头中会加入cookie信息。关于这个情况,在不同的浏览器下,会有所差异。
void | addCookie(Cookie cookie) |
1) 创建cookie对象;
2) 将cookie响应给客户端;
protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
Cookie cookie1=new Cookie("c1","hahah");
response.addCookie(cookie1);
response.getWriter().write("addCookieToClient,pleaseCheck");
}
3. 设置中文字符的cookie内容的编码与解码
Cookie cookie=new Cookie("username",URLEncoder.encode("使用utf-8对中文编码","utf-8"));
Cookie[] cookies=request.getCookies();
for(Cookie c:cookies){
if("username".equals(c.getName())){
String decode=URLDecoder.decode(c.getValue(),"utf-8");
System.out.println("解码后的cookie的值"+decode);
}
}
4. 获取客户端cookie信息
那么,客户端的cookie既然是存放在请求头中,那么通过request对象可以获得cookie信息。注意判断,request中cookie为空的情况。
Request对象的方法
Cookie[] | getCookies() |
Cookie类中的方法
getName() | |
getValue() | |
void | setValue(String newValue) |
例子:
protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
Cookie cookie1=new Cookie("c1","hahah");
Cookie cookies[]=request.getCookies();
if(cookies!=null){
for(Cookie cookie2:cookies){
String cc=cookie2.getName();
String cv=cookie2.getValue();
System.out.println(cc+"--"+cv);
}
}
response.addCookie(cookie1);
response.getWriter().write("addCookieToClient,pleaseCheck");
}
5. 设置cookie的生存时间
一般情况,cookie是临时性的,随着浏览器的关闭而清除。Cookie类中提供了设置cookie存活时间的方法。
void | setMaxAge(int expiry) |
其中的参数int expiry,以秒为单位,负数表示不存储cookie,浏览器关闭则消失;0表示立即清除cookie;正数表示持续存在多长时间,如60*60,即一个小时。
Cookie cookieCycle=new Cookie("shengming","haha");
//设置cookie的存活时间为6分钟
cookieCycle.setMaxAge(6*60);
response.addCookie(cookieCycle);
6. 设置cookie的响应路径
在响应对象添加cookie之前,可以给cookie设置响应成功后请求头可携带cookie的请求路径。Cookie的路径可以设置为其他项目或其子目录。
通过cookie对于转发,重定向的测试,得到一个现象,转发一般是多次请求,而重定向则只有一次请求,这也体现了转发能够将请求源贯穿到连续请求的情况。
1) 如果没有设置cookie的路径,那么当前cookie创建添加的目录及其子目录都可以使用。
2) 如果设置了cookie的路径,则设置cookie的路径目录及其子目录都可以使用。
3) 如果设置cookie的路径为其他项目,则本项目将访问服务端,将不能携带这个cookie。
4) 如果设置cookie的路径为/,则所有的以服务器ip:端口号开头的下的url路径都可以携带这个cookie。
例子:
//cookieCycle.setPath("/spring/haha");
cookieCycle.setPath("/");
7. 删除cookie
删除cookie,一般是将cookie的存活时间设为0,访问路径设为/,然后把这个设置后的cookie添加到服务器的响应对象。
Cookie cookieCycle=new Cookie("shengming","haha");
//设置cookie的存活时间为6分钟
cookieCycle.setMaxAge(0);
cookieCycle.setPath("/");
response.addCookie(cookieCycle);
三、 Session
Cookie使用http协议在web交互中传播,存在被截获的可能,有安全隐患。即使设置了cookie的secure属性,仍然不能保证绝对安全。虽然,在服务端发放cookie时采用加密的方式,可以提高安全性,但对于机密度很高的数据,仍然不够安全。保证用户交互数据相对安全的手段,是将这些数据存放在服务端,一方面减少了这些数据在web中传播,另一方面,一般服务端的安全性相对较高。但是,由于session保存在服务器,使用session增加了服务器的压力。在session与客户端的交互中,需要借助cookie技术传递用户的唯一标识id。在java中,使用HttpSession接口可以操作session。
1. JavaEE API
javax.servlet.http
Interface HttpSession
public interface HttpSession
提供一种方式,跨多个页面请求或对 Web 站点的多次访问标识用户并存储有关该用户的信息。
servlet 容器使用此接口创建HTTP 客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应于一个用户,该用户可能多次访问一个站点。服务器能够以多种方式维护会话,比如使用 cookie 或重写 URL。
此接口允许 servlet
查看和操作有关某个会话的信息,比如会话标识符、创建时间和最后一次访问时间
将对象绑定到会话,允许跨多个用户连接保留用户信息
当应用程序将对象存储到会话中或从会话中移除对象时,该会话将检查对象是否实现了 HttpSessionBindingListener。如果实现了,则 servlet 将通知该对象它已经被绑定到会话,或者已从会话中取消对它的绑定。通知是在绑定方法完成后发送的。对于无效或过期的会话,通知是在会话已经无效或过期之后发送的。
当容器使用分布式容器设置在 VM 之间迁移会话时,所有实现 HttpSessionActivationListener接口的会话属性都会得到通知。
servlet 应该能够处理客户端选择不加入会话的情况,比如故意关闭 cookie 时。在客户端加入会话前,isNew 一直返回 true。如果客户端选择不加入会话,则 getSession 将对每个请求返回一个不同的会话,并且 isNew 将总是返回 true。
会话信息的范围仅限于当前 Web 应用程序(ServletContext),因此存储在一个上下文中的信息在另一个上下文中不是直接可见的。
2. 获取session及为session赋值
Session由服务器创建,当用户请求服务器时,通过request对象可以获取这个session。
Request的方法
getSession() | |
getSession(boolean create) |
注意getSession(Boolean create)方法的含义。当参数为true时,如果没有session,则需要服务器创建一个session,如果为false或者使用了getSession,则不会创建了,那么返回的结果为null。
HttpSession的方法
void | setAttribute(String name, Object value) |
void | removeAttribute(String name) |
getAttribute(String name) |
例子:
protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
HttpSession session=request.getSession();
//将键值对放入session
session.setAttribute("haha", request.getRemoteAddr());
}
protected voiddoGet(HttpServletRequest request, HttpServletResponse response) throwsServletException, IOException {
String haha=(String)request.getSession().getAttribute("haha");
response.getWriter().write(haha);
}
3. Session的生命周期
Session作为跟踪客户端会话的技术,在区别不同的客户端方面,采用cookie技术存放一个唯一标识,所以,服务器取得客户端的session需要借助cookie。然后,cookie的存在有3中情况,1,随浏览器关闭清除,2,根据设置的时效清除,3,立即清除。所以,如果,关闭浏览器cookie清除,则无法获得这个客户端的session。通过持久化cookie,可以持久化session,前提是没有对session使用invalidate方法。
Session的创建时间:在服务器开启的前提下,客户端首次访问服务器时,通过servlet的getSession方法,服务器得到信号,就会创建这个客户端的session容器。
Session的销毁时间:A,非正常关闭服务器。在正常关闭服务器的时候,session会保存在服务器的某个文件中。
B,为了防止内存溢出,一般情况下,服务器会将不常活跃的session设置一个超时时间,超过一定时间没有访问服务器,则session失效。Session的超时时间可以通过setMaxInactiveInterval方法修改。也可以在web.xml中配置。C,调用session的invalidate方法同样可以使session失效。
void | setMaxInactiveInterval(int interval) |
setMaxInactiveInterval方法中参数的单位为秒。
Web.xml配置session有效期的例子:
<session-config>
<!-- 参数的单位为分钟 -->
<session-timeout>3</session-timeout>
</session-config>
四、 关于servlet存放数据的容器
ServletContext:项目下所有servlet共享,不具有针对性。
ServletSession:针对某个客户端的容器对象。适合客户端使用服务器服务期间的业务需求。
HttpServletRequest:针对客户端的某次请求,响应结束,这容器清空。适合请求期间的数据传输需求。