深入理解Session和cookie原理
Posted 想作会飞的鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Session和cookie原理相关的知识,希望对你有一定的参考价值。
一、概述
Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态。它们有各自的优点,也有各自的缺陷,然而具有讽刺意味的是它们的优点和它们的使用场景又是矛盾的。例如,使用 Cookie 来传递信息时,随着 Cookie 个数的增多和访问量的增加,它占用的网络带宽也很大,试想假如 Cookie 占用 200 个字节,如果一天的 PV 有几亿,它要占用多少带宽?所以有大访问量的时候希望用 Session,但是 Session 的致命弱点是不容易在多台服务器之间共享,所以这也限制了 Session 的使用。
二、理解 Cookie
Cookie 的作用我想大家都知道,通俗地说就是当一个用户通过 HTTP 协议访问一个服务器的时候,这个服务器会将一些 Key/Value 键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户下次访问这个服务器的时候,数据又被完整地带回给服务器。这个作用就像您去超市购物时,第一次给您办张购物卡,这个购物卡里存放了一些您的个人信息,下次您再来这个连锁超市时,超市会识别您的购物卡,下次直接购物就好了。
当初 W3C 在设计 Cookie 时实际上考虑的是为了记录用户在一段时间内访问 Web 应用的行为路径。由于 HTTP 协议是一种无状态协议,当用户的一次访问请求结束后,后端服务器就无法知道下一次来访问的还是不是上次访问的用户,在设计应用程序时,我们很容易想到两次访问是同一人访问与不同的两个人访问对程序设计和性能来说有很大的不同。例如,在一个很短的时间内,如果与用户相关的数据被频繁访问,可以针对这个数据做缓存,这样可以大大提高数据的访问性能。Cookie 的作用正是在此,由于是同一个客户端发出的请求,每次发出的请求都会带有第一次访问时服务端设置的信息,这样服务端就可以根据 Cookie 值来划分访问的用户了。
1、Cookie 属性项
当前 Cookie 有两个版本:Version 0 和 Version 1。通过它们有两种设置响应头的标识,分别是 “Set-Cookie”和“Set-Cookie2”。这两个版本的属性项有些不同。
- Version 0 属性项介绍
- Version 1 属性项介绍
以上两个版本的 Cookie 中设置的 Header 头的标识符是不同的,我们常用的是 Set-Cookie:userName=“junshan”; Domain=“xulingbo.net”
,这是 Version 0 的形式。针对 Set-Cookie2 是这样设置的:Set-Cookie2:userName=“junshan”; Domain=“xulingbo.net”; Max-Age=1000
。但是在 Java Web 的 Servlet 规范中并不支持 Set-Cookie2 响应头,在实际应用中 Set-Cookie2 的一些属性项却可以设置在 Set-Cookie 中,如这样设置:Set-Cookie:userName=“junshan”; Version=“1”;Domain=“xulingbo.net”;Max-Age=1000
。
2、Cookie 如何工作
当我们用如下方式创建 Cookie 时:
String getCookie(Cookie[] cookies, String key)
if (cookies != null)
for (Cookie cookie : cookies)
if (cookie.getName().equals(key))
return cookie.getValue();
return null;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
Cookie[] cookies = request.getCookies();
String userName = getCookie(cookies, "userName");
String userAge = getCookie(cookies, "userAge");
if (userName == null)
response.addCookie(new Cookie("userName", "junshan"));
if (userAge == null)
response.addCookie(new Cookie("userAge", "28"));
response.getHeaders("Set-Cookie");
Cookie 是如何加到 HTTP 的 Header 中的?当我们用 Servlet 3.0 规范来创建一个 Cookie 对象时,该 Cookie 既支持 Version 0 又支持 Version 1,如果您设置了 Version 1 中的配置项,即使您没有设置版本号,Tomcat 在最后构建 HTTP 响应头时也会自动将 Version 的版本设置为 1。下面看一下 Tomcat 是如何调用 addCookie 方法。真正构建 Cookie 是在 org.apache.catalina.connector. Response
类中完成的,调用 generateCookieString
方法将 Cookie 对象构造成一个字符串,构造的字符串的格式如 userName=“junshan”;Version=“1”; Domain=“xulingbo.net”; Max-Age=1000
。然后将这个字符串命名为 Set-Cookie 添加到 MimeHeaders 中。
在这里有几点需要注意:
创建的 Cookie 的 NAME 不能和 Set-Cookie 或者 Set-Cookie2 的属性项值一样,如果一样会抛 IllegalArgumentException 异常。
创建 Cookie 的 NAME 和 VALUE 的值不能设置成非 ASSIC 字符,如果要使用中文,可以通过 URLEncoder 将其编码,否则将会抛 IllegalArgumentException 异常。
当 NAME 和 VALUE 的值出现一些 TOKEN 字符(如“\\”、“,”等)时,构建返回头会将该 Cookie 的 Version 自动设置为 1。
当该 Cookie 的属性项中出现 Version 为 1 的属性项时,构建 HTTP 响应头同样会将 Version 设置为 1。
当我们通过 response.addCookie
创建多个 Cookie 时,这些 Cookie 最终是在一个 Header 项中还是以独立的 Header 存在的?通俗地说也就是我们每次创建 Cookie 时是否都是创建一个以 NAME 为 Set-Cookie 的 MimeHeaders ?答案是肯定的。每次调用 addCookie 的时候,最终都会创建一个 Header,但是我们还不知道最终在请求返回时构造 HTTP 响应头是否将相同 Header 标识的 Set-Cookie 值进行合并。
我们找到 Tomcat 最终构造 HTTP 响应头的代码,这段代码位于 org.apache.coyote.http11. Http11Processor
类的 prepareResponse
方法中,如下所示:
int size = headers.size();
for (int i = 0; i < size; i++)
outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
这段代码清楚地表示,在构建 HTTP 返回字节流时是将 Header 中所有的项顺序地写出,而没有进行任何修改。所以可以想象浏览器在接收 HTTP 协议返回的数据时是分别解析每一个 Header 项的。
前面主要介绍了服务端如何创建 Cookie,下面看一下如何从客户端获取 Cookie。
当我们请求某个 URL 路径时,浏览器会根据这个 URL 路径将符合条件的 Cookie 放在 Request 请求头中传回给服务端,服务端通过 request.getCookies() 来取得所有 Cookie。
3、使用 Cookie 的限制
Cookie 是 HTTP 协议头中的一个字段,虽然 HTTP 协议本身对这个字段并没有多少限制,但是 Cookie 最终还是存储在浏览器里,所以不同的浏览器对 Cookie 的存储都有一些限制。
三、理解 Session
前面已经介绍了 Cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些 Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,而 Session 的出现正是为了解决这个问题。
同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为 JSESIONID
的一个 Cookie。
1、Session 与 Cookie
下面详细讲一下 Session 如何基于 Cookie 来工作。实际上有三种方式能可以让 Session 正常工作:
基于 URL Path Parameter,默认支持。
基于 Cookie,如果没有修改 Context 容器的 cookies 标识,默认也是支持的。
基于 SSL,默认不支持,只有
connector.getAttribute("SSLEnabled")
为 TRUE 时才支持。
第一种情况下,当浏览器不支持 Cookie 功能时,浏览器会将用户的 SessionCookieName
重写到用户请求的 URL 参数中,它的传递格式如 /path/Servlet;name=value;name2=value2? Name3=value3
,其中“Servlet;”后面的 K-V(name=value;name2=value2) 就是要传递的 Path Parameters,服务器会从这个 Path Parameters 中拿到用户配置的 SessionCookieName
。关于这个 SessionCookieName
,如果在 web.xml 中配置 session-config 配置项,其 cookie-config 下的 name 属性就是这个 SessionCookieName
值。如果没有配置了 session-config 配置项,默认的 SessionCookieName
就是大家熟悉的“JSESSIONID”。需要说明的一点是,与 Session 关联的 Cookie 与其他 Cookie 没有什么不同。接着 Request 根据这个 SessionCookieName
到 Parameters 中拿到 Session ID 并设置到 request.setRequestedSessionId
中。
请注意,如果客户端也支持 Cookie,Tomcat 仍然会解析 Cookie 中的 Session ID,并会覆盖 URL 中的 Session ID。
如果是第三种情况,将会根据 javax.servlet.request.ssl_session
属性值设置 Session ID。
2、Session 如何工作
有了 Session ID 服务端就可以创建 HttpSession 对象了,第一次触发通过 request.getSession()
方法。如果当前的 Session ID 还没有对应的 HttpSession 对象,那么就创建一个新的,并将这个对象加到 org.apache.catalina. Manager
的 sessions 容器中保存。Manager 类将管理所有 Session 的生命周期,Session 过期将被回收,服务器关闭,Session 将被序列化到磁盘等。只要这个 HttpSession 对象存在,用户就可以根据 Session ID 来获取这个对象,也就达到了状态的保持。
StandardManager 类负责 Servlet 容器中所有的 StandardSession 对象的生命周期管理。当 Servlet 容器重启或关闭时 StandardManager 负责持久化没有过期的 StandardSession 对象,它会将所有的 StandardSession 对象持久化到一个以“SESSIONS.ser”为文件名的文件中。到 Servlet 容器重启时,也就是 StandardManager 初始化时,会重新读取这个文件解析出所有 Session 对象,重新保存在 StandardManager 的 sessions 集合中。Session 恢复状态图如图:
当 Servlet 容器关闭时 StandardManager 类会调用 unload 方法将 sessions 集合中的 StandardSession 对象写到“SESSIONS.ser”文件中,然后在启动时再按照上面的状态图重新恢复,注意要持久化保存 Servlet 容器中的 Session 对象,必须调用 Servlet 容器的 stop 和 start 命令,而不能直接结束(kill)Servlet 容器的进程,因为直接结束进程,Servlet 容器没有机会调用 unload 方法来持久化这些 Session 对象。
另外,StandardManager 中的 sessions 集合中的 StandardSession 对象并不是永远保存的,否则 Servlet 容器的内存将很容易被消耗尽,所以必须给每个 Session 对象定义一个有效时间,超过这个时间 Session 对象将被清除。在 Tomcat 中这个有效时间是 60(maxInactiveInterval 属性控制)秒,超过 60 秒该 Session 将会过期。检查每个 Session 是否失效是在 Tomcat 的一个后台线程中完成的(backgroundProcess() 方法中)。过期 Session 状态图如图 :
除了后台进程检查 Session 是否失效外,当调用 request.getSession()
时也会检查该 Session 是否过期。值得注意的是,request.getSession()
方法调用的 StandardSession 永远都会存在,即使与这个客户端关联的 Session 对象已经过期。如果过期,又会重新创建一个全新的 StandardSession 对象,但是以前设置的 Session 值将会丢失。如果您取到了 Session 对象但是通过 session.getAttribute
取不到前面设置的 Session 值,请不要奇怪,因为很可能它已经失效了,请检查一下 <Manager pathname="" maxInactiveInterval="60" />
中 maxInactiveInterval
配置项的值,如果不想让 Session 过期可以设置为 -1。但是您要仔细评估一下,网站的访问量和设置的 Session 的大小,防止将您的 Servlet 容器内存撑爆。如果不想自动创建 Session 对象,也可以通过 request.getSession(boolean create)
方法来判断该客户端关联的 Session 对象是否存在。
四、Cookie 安全问题
虽然 Cookie 和 Session 都可以跟踪客户端的访问记录,但是它们的工作方式显然是不同的,Cookie 通过把所有要保存的数据通过 HTTP 协议的头部从客户端传递到服务端,又从服务端再传回到客户端,所有的数据都存储在客户端的浏览器里,所以这些 Cookie 数据可以被访问到,就像我们前面通过 Firefox 的插件 HttpFox 可以看到所有的 Cookie 值。不仅可以查看 Cookie,甚至可以通过 Firecookie 插件添加、修改 Cookie,所以 Cookie 的安全性受到了很大的挑战。
相比较而言 Session 的安全性要高很多,因为 Session 是将数据保存在服务端,只是通过 Cookie 传递一个 SessionID 而已,所以 Session 更适合存储用户隐私和重要的数据。
五、分布式 Session 框架
从前面的分析可知,Session 和 Cookie 各自有优点和缺点。在大型互联网系统中,单独使用 Cookie 和 Session 都是不可行的,原因很简单。因为如果使用 Cookie,可以很好地解决应用的分布式部署问题,大型互联网应用系统一个应用有上百台机器,而且有很多不同的应用系统协同工作,由于 Cookie 是将值存储在客户端的浏览器里,用户每次访问都会将最新的值带回给处理该请求的服务器,所以也就解决了同一个用户的请求可能不在同一台服务器处理而导致的 Cookie 不一致的问题。
1、存在哪些问题
这种“谁家的孩子谁抱走”的处理方式的确是大型互联网的一个比较简单但是的确可以解决问题的处理方式,但是这种处理方式也会带来了很多其他问题,如:
客户端 Cookie 存储限制。随着应用系统的增多 Cookie 数量也快速增加,但浏览器对于用户 Cookie 的存储是有限制的。例如,IE7 之前的 IE 浏览器,Cookie 个数的限制是 20 个,后续的版本,包括 Firefox 等,Cookie 个数的限制都是 50 个。总大小不超过 4KB,超过限制就会出现丢弃 Cookie 的现象发生,这会严重影响应用系统的正常使用。
Cookie 管理的混乱。在大型互联网应用系统中,如果每个应用系统都自己管理每个应用使用的 Cookie,将会导致混乱,由于通常应用系统都在同一个域名下,Cookie 又有上面一条提到的限制,所以没有统一管理很容易出现 Cookie 超出限制的情况。
安全令人担忧。虽然可以通过设置 HttpOnly 属性防止一些私密 Cookie 被客户端访问,但是仍然不能保证 Cookie 无法被篡改。为了保证 Cookie 的私密性通常会对 Cookie 进行加密,但是维护这个加密 Key 也是一件麻烦的事情,无法保证定期来更新加密 Key 也是带来安全性问题的一个重要因素。
当以上问题不能再容忍下去的时候,就不得不想其他办法处理了。
2、可以解决哪些问题
既然 Cookie 有以上这些问题,Session 也有它的好处,为何不结合使用 Session 和 Cookie 呢?下面是分布式 Session 框架可以解决的问题:
Session 配置的统一管理。
Cookie 使用的监控和统一规范管理。
Session 存储的多元化。
Session 配置的动态修改。
Session 加密 key 的定期修改。
充分的容灾机制,保持框架的使用稳定性。
Session 各种存储的监控和报警支持。
Session 框架的可扩展性,兼容更多的 session 机制如 wapSession。
跨域名 Session 与 Cookie 如何共享,现在同一个网站可能存在多个域名,如何将 Session 和 Cookie 在不同的域名之间共享是一个具有挑战性的问题。
3、总体实现思路
分布式 Session 框架的架构图如图所示。
为了达成上面所说的几点目标,我们需要一个服务订阅服务器,在应用启动时可以从这个订阅服务器订阅这个应用需要的可写 Session 项和可写 Cookie 项,这些配置的 Session 和 Cookie 可以限制这个应用能够使用哪些 Session 和 Cookie,甚至可以控制 Session 和 Cookie 可读或者可写。这样可以精确地控制哪些应用可以操作哪些 Session 和 Cookie,可以有效控制 Session 的安全性和 Cookie 的数量。统一通过订阅服务器推送配置可以有效地集中管理资源,所以可以省去每个应用都来配置 Cookie,简化 Cookie 的管理。如果应用要使用一个新增 Cookie,可以通过一个统一的平台来申请,申请通过才将这个配置项增加到订阅服务器。如果是一个所有应用都要使用的全局 Cookie,那么只需将这个 Cookie 通过订阅服务器统一推送过去就行了,省去了要在每个应用中手动增加 Cookie 的配置。
关于这个订阅服务器现在有很多开源的配置服务器,如 Zookeeper 集群管理服务器,可以统一管理所有服务器的配置文件。
由于应用是一个集群,所以不可能将创建的 Session 都保存在每台应用服务器的内存中,因为如果每台服务器有几十万的访问用户,服务器的内存肯定不够用,即使内存够用,这些 Session 也无法同步到这个应用的所有服务器中。所以要共享这些 Session 必须将它们存储在一个分布式缓存中,可以随时写入和读取,而且性能要很好才能满足要求。当前能满足这个要求的系统有很多,如 MemCache 或者淘宝的开源分布式缓存系统 Tair 都是很好的选择。
解决了配置和存储问题,下面看一下如何存取 Session 和 Cookie。
既然是一个分布式 Session 的处理框架,必然会重新实现 HttpSession 的操作接口,使得应用操作 Session 的对象都是我们实现的 InnerHttpSession 对象,这个操作必须在进入应用之前完成,所以可以配置一个 filter 拦截用户的请求。
我们可以在应用的 web.xml 中配置一个 SessionFilter,用于在请求到达 MVC 框架之前封装 HttpServletRequest 和 HttpServletResponse 对象,并创建我们自己的 InnerHttpSession 对象,把它设置到 request 和 response 对象中。这样应用系统通过 request.getHttpSession()
返回的就是我们创建的 InnerHttpSession 对象了,我们可以拦截 response 的 addCookies 设置的 Cookie。应用创建的所有 Session 对象都会保存在 InnerHttpSession 对象中,当用户的这次访问请求完成时,Session 框架将会把这个 InnerHttpSession 的所有内容再更新到分布式缓存中,以便于这个用户通过其他服务器再次访问这个应用系统。另外,为了保证一些应用对 Session 稳定性的特殊要求可以将一些非常关键的 Session 再存储到 Cookie 中,如当分布式缓存存在问题时,可以将部分 Session 存储到 Cookie 中,这样即使分布式缓存出现问题也不会影响关键业务的正常运行。
还有一个非常重要的问题就是如何处理跨域名来共享 Cookie 的问题。我们知道 Cookie 是有域名限制的,也就是一个域名下的 Cookie 不能被另一个域名访问,所以如果在一个域名下已经登录成功,如何访问到另外一个域名的应用且保证登录状态仍然有效,这个问题大型网站应该经常会遇到。如何解决这个问题呢?答案是使用SSO单点登录系统。
以上方案就是要把信任关系存储在单独的SSO系统里,说起来只是简单地从客户端移到了服务端,但其中几个问题需要重点解决:
1、如何高效存储大量临时性的信任数据;
2、如何防止信息传递过程被篡改;
3、如何让SSO系统信任登录系统和免登系统。
对于第一个问题,一般可以采用类似与Redis的分布式缓存的方案,既能提供可扩展数据量的机制,也能提供高效访问。对于第二个问题,一般采取数字签名的方法,要么通过数字证书签名,要么通过像md5的方式,这就需要SSO系统返回免登URL的时候对需验证的参数进行md5加密,并带上token一起返回,最后需免登的系统进行验证信任关系的时候,需把这个token传给SSO系统,SSO系统通过对token的验证就可以辨别信息是否被改过。对于最后一个问题,可以通过白名单来处理,说简单点只有在白名单上的系统才能请求生产信任关系,同理只有在白名单上的系统才能被免登录。
六、表单重复提交问题
网站中在很多地方都有表单重复提交问题,一种情况是用户在网速慢的情况下可能会重复提交表单,还有就是恶意用户通过程序来发送恶意请求,在这些情况下都要设计一个防止表单重复提交的机制。
要能够防止表单重复提交,就要标识用户的每一次访问请求,使得每一次访问对服务端来说都是唯一确定的。为了标识用户的每次访问请求,可以在用户请求一个表单域时增加一个隐藏表单项,这个表单项的值每次都是唯一的 token,如:
<form id=”form” method=”post”>
<input type=hidden name=“crsf_token” value=“xxxx”/>
</form>
当用户在请求时生成这个唯一的 token 时,同时将这个 token 保存在用户的 Session 中,等用户提交请求时检查这个 token 和当前的 Session 中保存的 token 是否一致。如果一致,说明没有重复提交,否则用户提交上来的 token 已经不是当前的这个请求的合法 token。其工作过程如图所示。
生成唯一的 token 需要一个算法,最简单的就是可以根据一个种子作为 key 生成一个随机数,并保存在 Session 中,等下次用户提交表单时做验证。验证表单的过程如图所示。
七、Cookie和Session的区别
Cookie与Session都能够进行会话跟踪,但是完成的原理不太一样。普通状况下二者均能够满足需求,但有时分不能够运用Cookie,有时分不能够运用Session。下面经过比拟阐明二者的特性以及适用的场所。
1 .存取方式的不同
Cookie中只能保管ASCII字符串,假如需求存取Unicode字符或者二进制数据,需求先进行编码。Cookie中也不能直接存取Java对象。若要存储略微复杂的信息,运用Cookie是比拟艰难的。
而Session中能够存取任何类型的数据,包括而不限于String、Integer、List、Map等。Session中也能够直接保管Java Bean乃至任何Java类,对象等,运用起来十分便当。能够把Session看做是一个Java容器类。
2 .隐私策略的不同
Cookie存储在客户端阅读器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。
假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。最好是像Google、Baidu那样将Cookie信息加密,提交到服务器后再进行解密,保证Cookie中的信息只要本人能读得懂。而假如选择Session就省事多了,反正是放在服务器上,Session里任何隐私都能够有效的保护。
3.有效期上的不同
使用过Google的人都晓得,假如登录过Google,则Google的登录信息长期有效。用户不用每次访问都重新登录,Google会持久地记载该用户的登录信息。要到达这种效果,运用Cookie会是比较好的选择。只需要设置Cookie的过期时间属性为一个很大很大的数字。
由于Session依赖于名为JSESSIONID的Cookie,而Cookie JSESSIONID的过期时间默许为–1,只需关闭了阅读器该Session就会失效,因而Session不能完成信息永世有效的效果。运用URL地址重写也不能完成。而且假如设置Session的超时时间过长,服务器累计的Session就会越多,越容易招致内存溢出。
4.服务器压力的不同
Session是保管在服务器端的,每个用户都会产生一个Session。假如并发访问的用户十分多,会产生十分多的Session,耗费大量的内存。因而像Google、Baidu、Sina这样并发访问量极高的网站,是不太可能运用Session来追踪客户会话的。
而Cookie保管在客户端,不占用服务器资源。假如并发阅读的用户十分多,Cookie是很好的选择。关于Google、Baidu、Sina来说,Cookie或许是唯一的选择。
5 .浏览器支持的不同
Cookie是需要客户端浏览器支持的。假如客户端禁用了Cookie,或者不支持Cookie,则会话跟踪会失效。关于WAP上的应用,常规的Cookie就派不上用场了。
假如客户端浏览器不支持Cookie,需要运用Session以及URL地址重写。需要注意的是一切的用到Session程序的URL都要进行URL地址重写,否则Session会话跟踪还会失效。关于WAP应用来说,Session+URL地址重写或许是它唯一的选择。
假如客户端支持Cookie,则Cookie既能够设为本浏览器窗口以及子窗口内有效(把过期时间设为–1),也能够设为一切阅读器窗口内有效(把过期时间设为某个大于0的整数)。但Session只能在本阅读器窗口以及其子窗口内有效。假如两个浏览器窗口互不相干,它们将运用两个不同的Session。(IE8下不同窗口Session相干)
6.跨域支持上的不同
Cookie可以通过如下方式实现跨域名访问,例如将domain属性设置为“.xxx.com”,则“a.xxx.com”和“b.xxx.com”均能够访问该Cookie。事实上只要是以”.xxx.com”结尾的域名均可访问。跨域名Cookie如今被普遍用在网络中,例如Google、Baidu、Sina等。而Session则不会支持跨域名访问。Session仅在他所在的域名内有效。
仅运用Cookie或者仅运用Session可能完成不了理想的效果。这时应该尝试一下同时运用Cookie与Session。Cookie与Session的搭配运用在实践项目中会完成很多意想不到的效果。
参考文献
《深入分析JavaWeb技术内幕》 许令波著
以上是关于深入理解Session和cookie原理的主要内容,如果未能解决你的问题,请参考以下文章