1. Cookie

1.1 什么是Cookie

Cookie 是浏览器端的数据存储技术,实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
Cookie和Session - 图1

  1. Cookie 的用途
    • 保持用户登录状态
    • 163邮箱的7天免登陆
  2. Cookie 的缺点
    • Cookie 最为请求或响应报文发送,无形中增加了网络流量。
    • Cookie 是明文传送的安全性差。
    • 各个浏览器对 Cookie 有限制,使用上有局限。

      1.2 使用Cookie

      Java 中把 Cookie 封装成了 javax.servlet.http.Cookie. 类。每个 Cookie 都是该 Cookie 类的对象,服务器通过操作 Cookie 类对象对客户端 Cookie 进行操作。通过 request.getCookie() 获取客户端提交的所有 Cookie(以 Cookie[] 数组形式返回),通过 response.addCookie(Cookiecookie) 向客户端设置Cookie。

Cookie对象使用 key-value 属性对的形式保存用户状态,一个 Cookie 对象保存一个属性对,一个 request 或者 response 同时使用多个Cookie。因为Cookie 类位于包 javax.servlet.http.* 下面,所以 JSP 中不需要 import 该类。

Cookie 的有效期

  • Cookie 的 maxAge 决定着 Cookie 的有效期,单位为秒。如果 maxAge 属性为正数,则表示该 Cookie 会在 maxAge 秒后自动失效,浏览器会将 maxAge 为正数的 Cookie 持久化,即写到对应的 Cookie 文件中。无论客户关闭了浏览器还是电脑,只要还在 maxAge 秒之前,登录网站时该 Cookie 仍然有效。
  • 如果 maxAge 为负数,则表示该 Cookie 仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该 Cookie 即失效。maxAge 为负数的 Cookie,为临时性Cookie,不会被持久化,不会被写到 Cookie 文件中。
  • 如果 maxAge 为0,则表示删除该 Cookie。Cookie 机制没有提供删除 Cookie的方法,因此通过设置该 Cookie 即时失效实现删除 Cookie 的效果。失效的 Cookie会被浏览器从 Cookie 文件或者内存中删除,
    1. Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
    2. cookie.setMaxAge(Integer.MAX_VALUE); // 设置生命周期为MAX_VALUE
    3. response.addCookie(cookie); // 输出到客户端

Cookie 的修改和删除:

  • Cookie 并不提供修改、删除操作。如果要修改某个 Cookie,只需要新建一个同名的 Cookie,添加到 response 中覆盖原来的 Cookie。如果要删除某个 Cookie,只需要新建一个同名的 Cookie,并将 maxAge 设置为0,并添加到 response 中覆盖原来的 Cookie。注意:修改、删除 Cookie 时,新建的 Cookie 除 value、maxAge 之外的所有属性,例如name、path、domain 等,都要与原 Cookie 完全一样。否则,浏览器将视为两个不同的 Cookie 不予覆盖,导致修改、删除失败。

Cookie 的路径:

  • domain 属性决定运行访问 Cookie 的域名,而 path 属性决定允许访问 Cookie 的路径(ContextPath)。例如如果只允许 /sessionWeb/ 下的程序使用 Cookie。
  • 设置为 “/” 时允许所有路径使用 Cookie。path 属性需要使用符号 “/” 结尾。
  • 注意:页面只能获取它属于的 Path 的 Cookie。例如 /session/test/a.jsp 不能获取到路径为 /session/abc/的Cookie。
    1. Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
    2. cookie.setPath("/session/"); // 设置路径
    3. response.addCookie(cookie); // 输出到客户端

Cookie 的安全性:

  • 使用 HTTP 协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。如果不希望 Cookie 在 HTTP 等非安全协议中传输,可以设置 Cookie 的 secure 属性为 true。浏览器只会在 HTTPS 和 SSL 等安全协议中传输此类 Cookie。
    1. Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
    2. cookie.setSecure(true); // 设置安全属性
    3. response.addCookie(cookie); // 输出到客户端

    1.3 Cookie原理

    cookie 技术通过在请求和响应报文中写入cookie 信息来控制客户点的状态,cookie 会根据从服务器端发送的响应报文内的一个叫做 set-cookie 的首部字段信息,通知客户端保存 cookie,当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 cookie 值后发送出去服务器端发现客户端发送过来的cookie后,会去检查究竟是从哪一个客户端发来的请求,然后对比服务器上的纪录最后得到之前的状态信息。

2. Session

2.1 什么是Session

使用 Cookie 有一个非常大的局限,就是如果 Cookie 很多,则无形的增加了客户端与服务端的数据传输量。而且由于浏览器对 Cookie 数量的限制,注定我们不能再Cookie 中保存过多的信息,于是 Session 出现。

Session 的作用就是在服务器端保存一些用户的数据,然后传递给用户一个名字为 JSESSIONID 的 Cookie,这个 JESSIONID 对应这个服务器中的一个 Session 对象,通过它就可以获取到保存用户信息的 Session。

如果说 Cookie 机制是通过检查客户身上的“通行证”来确定客户身份的话,那么 Session 机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session 相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。

2.2 Session原理

Session 的创建

  • Session 的创建时机是 在 request.getSession() 方法第一次被调用时。
  • Session 被创建后,同时还会有一个名为 JSESSIONID 的 Cookie 被创建。
  • 这个 Cookie 的默认时效就是当前会话。

Session的使用

  • Session 被创建后,对应的 Cookie 被保存到浏览器中,之后浏览器每次访问项目时都会携带该 Cookie。
  • 当我们再次调用时会根据该 JSESSIONID 获取已经存在的 Cookie,而不是在创建一个新的 Cookie。
  • 如果 Cookie 中有 JSESSIONID,但是 JSESSIONID 没有对应的 Session 存在,则会重新创建一个 HttpSession 对象,并重新设置 JSESSIONID。

    2.3 Session时效

    Session对象在服务器端不能长期保存,它是有时间限制的,超过一定时间没有被访问过的 Session 对象就应该释放掉,以节约内存。所以 Session 的有效时间并不是从创建对象开始计时,到指定时间后释放——而是从最后一次被访问开始计时,统计其“空闲”的时间。

默认设置:在全局 web.xml 中能够找到如下配置:30。说明 Session 对象默认的最长有效时间为 30 分钟。

手工设置:

  • session.setMaxInactiveInterval(int seconds)
  • session.getMaxInactiveInterval()

强制失效:

  • session.invalidate()

可以使 Session 对象释放的情况:

  • Session 对象空闲时间达到了目标设置的最大值,自动释放。
  • Session 对象被强制失效。
  • Web 应用卸载。
  • 服务器进程停止。

    2.4 URL重写

    在整个会话控制技术体系中,保持 JSESSIONID 的值主要通过 Cookie 实现。但 Cookie 在浏览器端可能会被禁用,所以我们还需要一些备用的技术手段,例如:URL重写。

URL 重写其实就是将 JSESSIONID 的值以固定格式附着在 URL 地址后面,以实现保持 JSESSIONID,进而保持会话状态。这个固定格式是:URL;jsessionid=xxxxxxxxx,例如:

  1. //1.获取Session对象
  2. HttpSession session = req.getSession();
  3. //2.创建目标URL地址字符串
  4. String url = "targetServlet";
  5. //3.在目标URL地址字符串后面附加JSESSIONID的值
  6. url = resp.encodeURL(url);
  7. //4.重定向到目标资源
  8. resp.sendRedirect(url);

2.5 Session的活化与钝化

Session 机制很好的解决了 Cookie 的不足,但是当访问应用的用户很多时,服务器上就会创建非常多的 Session 对象,如果不对这些 Session 对象进行处理,那么在Session 失效之前,这些 Session 一直都会在服务器的内存中存在。那么就,就出现了 Session 活化和钝化的机制。

  • Session 钝化:Session 在一段时间内没有被使用时,会将当前存在的 Session 对象序列化到磁盘上,而不再占用内存空间。
  • Session 活化:Session 被钝化后,服务器再次调用 Session 对象时,将Session对象由磁盘中加载到内存中使用。
  • 如果希望 Session 域中的对象也能够随 Session 钝化过程一起序列化到磁盘上,则对象的实现类也必须实现 java.io.Serializable 接口。不仅如此,如果对象中还包含其他对象的引用,则被关联的对象也必须支持序列化,否则会抛出异常:java.io.NotSerializableException

    2.6 Session共享

    为什么要实现 Session 共享?

假设某个网站是由多台服务器提供服务,nginx 采用轮询机制做负载均衡,那么同一个 IP 访问该网站时,请求就可能会被分配到不同的服务器上,如果 session 没有实现共享,就会出现重复登陆授权的情况。

实现 Session 共享

1、基于 NFS 的 session 共享
NFS(Net FileSystem),顾名思义,也就是网络文件系统,即将 session 存储目录挂载到所有的服务器上,实现session的读写。该方案实现简单、快速,无需在应用程序上做太多操作。但是缺点也很明显,一是只适合小规模的网站,二是文件读写速度严重依赖网络速度,几台服务器在同一局域网内还能接受,如果跨机房就很容易受到影响而且安全问题也是需要考虑的,三是需要实时监控每一台服务器目录挂载情况。

2、基于 Cookie 的 session 共享
简单的说就是登陆时,将创建的 session 信息经过加密处理后一并保存在客户端的 Cookie 里,其他服务器响应请求时,先判断本地服务器是否有已保存的 session,如果没有就再将客户端的 Cookie 里保存的 session 信息同步到本地服务器。这种方式的缺点就是受客户端的影响较大,而且安全性无法得到保障。

3、基于数据库的 session 共享
该方案就是将 session 数据直接存储在数据库中。该方案操作性较强、安全性高,缺点就是 session 的并发读写能力取决于数据库的性能,而且需要应用程序支持或者编写一个额外的维护脚本,实现 session 的更新和过期删除。

4、基于 Memcache/Redis 的 session 共享
该方案时可以看作时方案三的升级版,该方案不仅性能好而且安全性也得到了保障,更重要的时 Memcache/Redis 原生支持数据的 expire 过期淘汰。

3. 二者区别

  1. session 在服务器端,cookie 在客户端(浏览器)。
  2. session 默认被存在在服务器的一个文件里(不是内存)。
  3. session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)。
  4. session 可以放在 文件、数据库、或内存中都可以。
  5. 用户验证这种场合一般会用 session

因此,维持一个会话的核心就是客户端的唯一标识,即 session id。