状态管理-Cookie
状态管理
1.1. 为什么需要状态管理
Web应用程序使用HTTP协议作为传输数据的标准协议,而HTTP协议是无状态协议,即一次请求对应一次响应,响应结束后连接即断开,同一个用户的不同请求对于服务器端来讲并不会认为这两个请求有什么关联性,并不会以此区分不同的客户端。但实际情况中还是需要服务器端能够区分不同的客户端以及记录与客户端相关的一些数据,所以状态管理能够做到不同客户端的身份识别。
1.2. 什么是状态管理
将客户端与服务器之间多次交互当做一个整体来看待,并且将多次交互中涉及的数据保存下来,提供给后续的交互进行数据的管理即状态管理。
这里的状态指的是当前的数据,管理指的是在这个多次交互的过程中对数据的存储、修改、删除。
生活中很多与状态管理类似的案例。如洗车卡记录洗车次数就是很典型的状态管理。洗车卡可以是一张记录简单次数的标示,车主每次携带卡片洗车后由商家修改,车主即可带走这张记录数据的卡片,商家不会保存任何数据,客户自己负责携带需要维护的数据。还有一种处理方式就是商家只给客户一个卡号,每次客户来洗车时自己不会记录洗车次数,只要报上卡号,商家就会从系统中找到与卡号对应的数据,修改后仍然是商家保存,客户带走的只是一个卡号这个标示。
以上两种模式都能实现洗车次数的记录,也就是数据的管理,只是各有利弊,程序中的状态管理与这个案例都采用了同样的处理原理。
1.3. 状态管理两种常见模式
状态管理的过程中重要的是数据的保存,只有存下来的数据才能在多次交互中起到记录的作用,所以可以按照管理的数据的存储方式和位置的不同来区分状态管理的模式。
如果将数据存储在客户端,每次向服务器端发请求时都将存在客户端的数据随着请求发送到服务器端,修改后再发回到客户端保存的这种模式叫做Cookie。
如果将数据存储在服务器端,并且为这组数据标示一个编号,只将编号发回给客户端。当客户端向服务器发送请求时只需要将这个编号发过来,服务器端按照这个编号找到对应的数据进行管理的这种模式叫做Session——会话。
cookie
由于Cookie数据是由客户端来保存和携带的,所以称之为客户端技术。
创建一个cookie,cookie是servlet发送到web浏览器的少量信息,这些信息由浏览器保存,然后发送回服务器。cookie的值可以唯一标识客户端,因此cookie常用于会话管理。
一个cookie拥有一个名称、一个值和一些可选属性,比如注释、路径和域限定符。最大生存时间和版本号。一些浏览器在处理可选属性方面存在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 中定义的缓存控件。
属性:
- name:名称不能唯一确定一个Cookie。路径可能不同。
- value:不能存中文。
-
path:默认值是写Cookie的那个程序的访问路径
例子:
比如:http://localhost:8080/cookie/servlet/ck1写的Cookie- path就是:/cookie/servlet 看当前创建cookie的资源(servlet)文件路径
- 客户端在访问服务器另外资源时,根据访问的路径来决定是否带着Cookie到服务器
- 当前访问的路径如果是以cookie的path开头的路径,浏览器就带。否则不带。
Cookie c = new Cookie(“uname”,“jack”);
c.setPath(“/test”);
response.addCookie(c); -
* maxAge*:cookie的缓存时间。默认是-1(默认存在浏览器的内存中)。单位是秒。
- 负数:cookie的数据存在浏览器缓存中
- 0:删除。路径要保持一致,否则可能删错人。
- 正数:缓存(持久化到磁盘上)的时间
方法:
public String getName()
返回 cookie 的名称。名称在创建之后不得更改。public void setValue(String newValue)
在创建 cookie 之后将新值分配给 cookie。如果使用二进制值,则可能需要使用 BASE64 编码。
对于 Version 0 cookie,值不应包含空格、方括号、圆括号、等号、逗号、双引号、斜杠、问号、at 符号、冒号和分号。空值在所有浏览器上的行为不一定相同。public void setPath(String uri)
指定客户端应该返回 cookie 的路径。
cookie 对于指定目录中的所有页面及该目录子目录中的所有页面都是可见的。cookie 的路径必须包括设置 cookie 的 servlet,例如 /catalog,它使 cookie 对于服务器上 /catalog 下的所有目录都是可见的。public String getPath()
返回浏览器将此 cookie 返回到的服务器上的路径。cookie 对于服务器上的所有子路径都是可见的。public void setMaxAge(int expiry)
设置 cookie 的最大生存时间,以秒为单位。
正值表示 cookie 将在经过该值表示的秒数后过期。注意,该值是 cookie 过期的最大 生存时间,不是 cookie 的当前生存时间。
负值意味着 cookie 不会被持久存储,将在 Web 浏览器退出时删除。0 值会导致删除 cookie。
编码
cookie只能保存合法的Ascii字符。如果要保存中文,需要将中文转换成合法的Ascii字符,及编码
Cookie c = new Cookie ("city", URLEncoder.encode("北京","UTF-8"));
cookie解码
编码后的Cookie为了看到实际的中文,需要还原后再显示
Cookie[] cookies=request,getCookies();
if(cookies!=null){
Cookies c =Cookie[0];
String value=c.getValue();
value=URLDecoder.decode(value , "UTF-8");
}
Cookie的限制
Cookie由于存放的位置在客户端,所以可以通过修改设置被用户禁止。Cookie本质就是一小段文本,一小段说的是只能保存少量数据,长度是有限制的,一般为4kb左右。文本说的是只能保存字符串,不能保留复杂的对象类型数据。
作为网络中传输的内容,Cookie安全性很低,非常容易通过截取数据包来获取,在没有加密的情况下不要用于存放敏感数据。
就算是能够存放的长度很短,但作为网络中传输的内容也会增加网络的传输量影响带宽。在服务器处理大量请求的时候,Cookie的传递无疑会增加网络的负载量。
session
HttpSession(全名):
它也是一个域对象: session servletContext request
同一个会话下,可以使一个应用的多个资源共享数据
cookie客户端技术,只能存字符串。HttpSession服务器端的技术,它可以存对象
工作原理:浏览器第一次访问服务器时,服务器会为该客户端分配一块对象空间,并且使用不同的SID来进行标识,该标识SID会随着响应发回到客户端,且被保存在内存中。当同一个客户端再次发送请求时,标识也会被同时发送到服务器端,而服务器判断要使用哪一个Session对象内的数据时,就会根据客户端发来的这个SID来进行查找。
获取Session
获得session有两种情况,要么请求中没有SID,则需要创建;要么请求中包含一个SID,根据SID去找对应的对象,但也存在找到找不到的可能。
但不管哪种情况都依赖于请求中的这个唯一标识,虽然对于编程人员来讲不需要去查看这个基本不会重复、编号很长的标识,但要想获取到与客户端关联的这个session对象一定要基于请求,
所以在Request类型的API中包含获取到session对象的方法,代码如下所示:
HttpSession s = request.getSession(boolean flag);
HttpSession s = request.getSession( );
使用第一种获取session对象的方法时,
flag = true:先从请求中找找看是否有SID,没有会创建新Session对象,有*SID会查找与编号对应的对象,找到匹配的对象则返回,找不到SID对应的对象时则会创建新Session对象。所以,填写true就一定会得到一个Session对象*。
flag= false:不存在SID以及按照SID找不到Session对象时都会返回null,只有根据SID找到对应的对象时会返回具体的Session对象。所以,填写false只会返回已经存在并且与SID匹配上了的Session对象。
request.getSession()方法不填写参数时等同于填写true,提供该方法主要是为了书写代码时更方便,大多数情况下还是希望能够返回一个Session对象的。
使用Session绑定对象
Session作为服务器端为各客户端保存交互数据的一种方式,采用name-value对的形式来区分每一组数据。
Session设置属性值
void session.setAttribute(String name,Object obj);
获取绑定数据或移除绑定数据的代码如下:
void session.getAttribute(String name);
void session.removeAttribute(String name);
删除Session对象
如果客户端想删除SID对应的Session对象时,可以使用Session对象的如下方法:
void invalidate()
该方法会使得服务器端与该客户端对应的S**ession对象不再被Session容器管理**,进入到垃圾回收的状态。对于这种立即删除Session对象的操作主要应用于不再需要身份识别的情况下,如登出操作。
Session超时
2.1. 什么是Session超时
Session会以对象的形式占用服务器端的内存,过多的以及长期的消耗内存会降低服务器端的运行效率,所以Session对象存在于内存中时会有默认的时间限制,一旦Session对象存在的时间超过了这个缺省的时间限制则认为是Session超时,Session会失效,不能再继续访问。
Web服务器缺省的超时时间设置一般是30分钟。
2.2. 如何修改Session的缺省时间限制
有两种方式可以修改Session的缺省时间限制,编程式和声明式。
编程式:
void setMaxInactiveInterval(int seconds)
声明式(再web.xml声明):
<session-config>
<session-timeout>30</session-timeout>
</session-config>
- 使用声明式来修改缺省时间,那么该应用创建的所有Session对象的生命周期都会应用这个规定的时间,单位为分钟。
- 使用编程式来修改缺省时间只会针对调用该方法的Session对象应用这一原则,不会影响到其他对象,所以更灵活。通常在需要特殊设置时使用这种方式。时间单位是秒,与声明式的时间单位不同。
浏览器禁用Cookie的后果
Session对象的查找依靠的是SID,而这个ID保存在客户端时是以Cookie的形式保存的。一旦浏览器禁用Cookie,那么SID无法保存,Session对象将不再能使用。
为了在禁用Cookie后依然能使用Session,那么将使用其他的存储方法来完成SID的保存。URL地址在网络传输过程中不仅仅能够起到标示地址的作用,还可以在其后携带一些较短的数据,SID就可以通过URL来实现保存,及URL重写。
URL重写
浏览器在访问服务器的某个地址时,会使用一个改写过的地址,即在原有地址后追加SessionID,这种重新定义URL内容的方式叫做URL重写。
如:原有地址的写法为http://localhost:8080/test/some
重写后的地址写法为http://localhost:8080/test/some;jsessionid=4E113CB3
如何实现URL重写
生成链接地址和表单提交时,使用如下代码:
<a href=”<%=response.encodeURL(String url)>”>链接地址
如果是重定向,使用如下代码代替response.sendRedirect()
response.encodeRedirectURL(String url);
Cookie 和Session的优缺点
Session对象的数据由于保存在服务器端,并不在网络中进行传输,所以安全一些,并且能够保存的数据类型更丰富,同时Session也能够保存更多的数据,Cookie只能保存大约4kb的字符串。
Session的安全性是以牺牲服务器资源为代价的,如果用户量过大,会严重影响服务器的性能。Cookie数据存在浏览器中,容易被篡改,安全性差,但节约服务器的内存。所以重要数据存储在session中,一般数据存在cookie中。