背景:
- HTTP协议是一种无状态的协议,WEB服务器本身不能识别出哪些请求是同一个浏览器发出的 ,浏览器的每一次请求都是完全孤立的
- 即使 HTTP1.1 支持持续连接,但当用户有一段时间没有提交请求,连接也会关闭。
- 作为 web 服务器,必须能够采用一种机制来唯一地标识一个用户,同时记录该用户的状态
会话和会话状态
- WEB应用中的会话是指一个客户端浏览器与WEB服务器之间连续发生的一系列请求和响应过程。
- WEB应用的会话状态是指WEB服务器与浏览器在会话过程中产生的状态信息,借助会话状态,WEB服务器能够把属于同一会话中的一系列的请求和响应过程关联起来。
实现有状态的会话
-
WEB服务器端程序要能从大量的请求消息中区分出哪些请求消息属于同一个会话,即能识别出来自同一个浏览器的访问请求,这需要浏览器对其发出的每个请求消息都进行标识:属于同一个会话中的请求消息都附带同样的标识号,而属于不同会话的请求消息总是附带不同的标识号,这个标识号就称之为会话ID(SessionID)。
-
在 Servlet 规范中,常用以下两种机制完成会话跟踪
Cookie
Session
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。
Cookie
- cookie机制采用的是在客户端保持 HTTP 状态信息的方案
- cookie是在浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头中附带传送给浏览器的一个小文本文件。
- 一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都会在HTTP请求头中将这个Cookie回传给WEB服务器。
- 底层的实现原理: WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie信息发送给浏览器,浏览器则通过在HTTP请求消息中增加Cookie请求头字段将Cookie回传给WEB服务器。
- 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
- 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
- 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
在Servlet程序中使用Cookie
- Servlet API中提供了一个javax.servlet.http.Cookie类来封装Cookie信息,它包含有生成Cookie信息和提取Cookie信息的各个属性的方法。
- Cookie类的方法:
构造方法: public Cookie(String name,String value)
getName方法
setValue与getValue方法
setMaxAge与getMaxAge方法
setPath与getPath方法
- HttpServletResponse接口中定义了一个addCookie方法,它用于在发送给浏览器的HTTP响应消息中增加一个Set-Cookie响应头字段。
- HttpServletRequest接口中定义了一个getCookies方法,它用于从HTTP请求消息的Cookie请求头字段中读取所有的Cookie项。
Cookie的工作原理
-
创建cookie对象
-
设置cookie生效时间
- 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie,即
会话cookie
,存储在浏览器的内存中,打开新的页面不会删除,但是用户退出浏览器之后将被删除。若希望浏览器再次打开时这些cookie依然有效,则需将它变为持久cookie
,需要使用setMaxAge方法给其设置一个以秒为单位的有效时间。此时该cookie将会被存储在磁盘上,关闭后再次打开浏览器,这些cookie依然有效,直到超过设定的有效时间。但是需要注意,如果最大时效设为0,则代表命令浏览器删除该cookie。 - 存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式
- 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie,即
-
将cookie放入到HTTP响应报头:发送cookie需要使用HttpServletResponse的addCookie方法,将cookie插入到一个 Set-Cookie HTTP响应报头中。
-
domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径,在相同路径的页面下,浏览器发送请求到服务器,将会自动带上这个cookie
Cookie的使用
- 调用request.getCookies
- 要获取浏览器发送来的cookie,需要调用HttpServletRequest的getCookies方法,这个调用返回Cookie对象的数组,对应由HTTP请求中Cookie报头输入的值。
- 对返回的Cookie对象数组进行循环,调用每个cookie的getName方法,找到目标cookie
// 1. 获取 Cookie
Cookie [] cookies = request.getCookies();
if(cookies != null && cookies.length > 0){
for(Cookie cookie: cookies){
// 2. 获取 Cookie 的 name 和 value
system.out.print(cookie.getName() + ": " + cookie.getValue());
system.out.print("<br>");
}
}else{
system.out.print("没有一个 Cookie, 正在创建并返回");
// 1. 创建一个 Cookie 对象
Cookie cookie = new Cookie("name", "zhangsan");
// 指定cookie绑定的路径
cookie.setPath(request.getContextPath() + "/text");
// setMaxAge: 设置 Cookie 的最大时效, 以秒为单位,
// 等于 0, 表示Cookie生成后马上失效
// 小于 0, 与不设置效果一样,Cookie将被存储在浏览器内存, 浏览器关闭即失效
// 大于 0, Cookie将被存储在磁盘,过期自动失效
cookie.setMaxAge(60 * 60);
// 2. 调用 response 的一个方法把 Cookie 传给客户端.
response.addCookie(cookie);
}
流程图
Session
- session机制采用的是在服务器端保持 HTTP 状态信息的方案 。
- session通过sessionID来区分不同的客户, session是以cookie或URL重写为基础的,默认使用cookie来实现,系统会创造一个名为JSESSIONID的输出cookie,这称之为session cookie,以区别persistent cookies(也就是我们通常所说的cookie),session cookie是存储于浏览器内存中的,并不是写到硬盘上的,通常看不到JSESSIONID,但是当把浏览器的cookie禁止后,web服务器会采用URL重写的方式传递Sessionid,这时地址栏看到
- session cookie针对某一次会话而言,会话结束session cookie也就随着消失了,而persistent cookie只是存在于客户端硬盘上的一段文本。
- 关闭浏览器,只会是浏览器端内存里的session cookie消失,但不会使保存在服务器端的session对象消失,同样也不会使已经保存到硬盘上的持久化cookie消失。
- 由于cookie可以被人为的禁用,必须有其它的机制以便在cookie被禁用时仍然能够把session id传递回服务器,经常采用的一种技术叫做URL重写,就是把session id附加在URL路径的后面,附加的方式也有两种,一种是作为URL路径的附加信息,另一种是作为查询字符串附加在URL后面。网络在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个session id。
在Servlet程序中使用Session
-
HttpServletRequest接口提供了以下方法来获取session对象:
public HttpSession getSession() :该方法取得请求所在的会话。
public HttpSession getSession(Boolean create):返回当前请求的会话。如果当前请求不属于任何会话,而且create参数为true,则创建一个会话,否则返回null。此后所有来自同一个的请求都属于这个会话,通过它的getSession返回的是当前会话。
-
session对象上的方法:
HttpSession session = request.getSession()
// 是否是新建session
isNew()
//销毁当前Session对象
invalidate()
//属性相关
setAttribute()
getAttribute()
removeAttribute()
getAttributeNames()
Session的工作原理
-
写入Session列表:
- 服务器对当前应用中的Session是以Map的形式进行管理的,这个Map称为Session列表。该Map的key是一个32位长度的随机字符串,也就是JSessionID,Map的value则为session对象的引用
- 当用户第一次提交请求时,服务端Servlet中执行到request.getSession()方法之后,会自动生成一个Map.Entry对象,key为某种算法生成的JSessionID,value为新创建的HttpSession,即session对象
-
服务器生成并发送Cookie:
- 在将Session信息写入Session列表之后,系统还会自动以"JSESSIONID"作为name,这个32为长度的JSessionID最为value,以Cookie的形式存放到响应报头中,并随着响应将该Cookie发送到客户端
-
客户端接收并发送Cookie:
- 客户端接收到这个Cookie后,会将其存放到浏览器的缓存中,只要客户端浏览器不关闭(即会话关闭),浏览器缓存中的Cookie就不会消失
- 当用户提交第二次请求时,会将缓存中的这个Cookie,伴随着请求的头部信息一块发送到服务端
-
从Session列表中查找
- 服务端从请求中读取到客户端发来的Cookie,并根据Cookie的JSESSIONID的值,也就是根据SessionId,从Map中查找相应key所对应的value,即Session对象,然后,对该Session对象的域属性进行读写操作
-
Session的失效
- Session的失效指的就是Session的超时,若某个Session在指定的时间范围内一直未被访问,那么Session将会失效
- Session默认的超时时间为30分钟,这个时间也可通过代码设置,需要注意的是这个超时时间并不是从Session被创建开始计时的,而是从最后一次被访问开始计时
- 若未到超时时限,也可以通过代码提前使Session失效。可以使用HttpSession中的invalidate()方法,销毁session,从而使session失效
- Session不使用的话需及时销毁,否则在原用户退出浏览器后,Session没有及时销毁的话,别人拿到SessionID,就可以使用该用户的Session,造成安全问题
Session的使用
- A接口调用 request.getSession 创建一个session对象,并向session域中写入某些属性
- 属于同一会话的页面调用B接口,B接口中调用 request.getSession 获取已存在的session对象,然后读取此对象中存取的属性值
// A接口
// 获取用户提交的参数
String username = request.getParameter("username");
// 将参数放入request域
request.setAttribute("user", username);
// 获取session对象,有就获取,没就创建
HttpSession session = request.getSession()
// 向session域中写入属性,同一会话的其他页面可以获取该属性
session.setAttribute("username", username);
system.out.print(username);
// B接口
// 获取session,因为此处需要读取session中的属性,所以传false即可
HttpSession session = request.getSession(false);
String username = ""
// 从session中读取指定属性
if (session != null) {
username = session.getAttribute("username");
}
system.out.print(username);
流程图
总结:
Cookie的优缺点:
- 优点:
- 极高的扩展性和可用性
- 通过良好的编程,控制保存在cookie中的session对象的大小。
- 通过加密和安全传输技术(SSL),减少cookie被破解的可能性。
- 只在cookie中存放不敏感数据,即使被盗也不会有重大损失。
- 控制cookie的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的cookie。
- 缺点:
-
cookie数量和长度的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉。
-
安全性问题。如果cookie被人拦截了,那人就可以取得所有的session信息。即使加密也与事无补,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。
-
有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。
-
Session的优缺点:
- 优点
- 可以是任何格式,存储量理论上是无限的,数据难以被获取、篡改、不易丢失
- 如果要在诸多Web页间传递一个变量,那么用Session变量要比通过QueryString传递变量可使问题简化
- 你可以在任何想要使用的时候直接使用session变量,而不必事先声明它
- 缺点
- 占用服务器资源
- 当一个用户访问某页面时,每个Session变量的运行环境便自动生成,这些Session变量可在用户离开该页面后仍保留30分钟!(“timeout”的时间长短由Web服务器管理员设定。一些站点上的变量仅维持了3分钟,一些则为10分钟,还有一些则保留至默认值30分钟。)所以,如果在Session中置入了较大的对象,那就有麻烦了!随着站点访问量的增大,服务器将会因此而无法正常运行!
- 因为创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。