废话部分
记得以前面试的时候,有个面试题就是让我说说cookie和session的区别。那时候只知道它们都是用来记录浏览器缓存的,然后一个是记录在服务器,一个是记录在客户端的,然后有效期、安全性也不一样等等。但是感觉有点像应试式的死记硬背,并没有完全理解为什么。所以今天又看到了这道面试题,就想着重新理解一下这里面的概念,并整理成文章,希望能够帮助到需要的人。
正文部分
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。
Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话。栗子:小明登录了淘宝,然后浏览淘宝信息、购买商品,这属于一个会话,小李登录淘宝进行的一系列操作又应该属于另一个会话了。
Web应用程序是使用HTTP协议传输数据的,而HTTP 是无状态的协议,对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息,所以每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。服务器如果单从网络连接上是无法知道客户身份。所以服务器与浏览器为了进行会话跟踪(知道客户的身份),于是就给每个客户端都颁发一个唯一的通行证,无论谁访问服务端都必须携带自己的通行证,这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。有点类似于我们现在做高铁进站要刷身份证和人脸认证的意思,只有对应的人和对应的身份证以及对应的车次才能进站,你刷别人的和别人刷你的身份证都不能进站。
2.1 什么是 Cookie
Cookie中文是“饼干”的意思,它是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。
- cookie 存储在客户端:cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
- cookie 是不可跨域的:很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。但是一级域名和二级域名之间是允许共享使用的,这里靠的是 domain。
- cookie 重要的属性
属性名 | 描述 |
name:value | 键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型name:该Cookie的名称,Cookie一旦创建,名称便不可更改value:该Cookie的值。如果值为 Unicode 字符,需要为字符编码。如果值为二进制数据,则需要使用 BASE64 编码。 |
version | 该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范 |
domain | 指定可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.” |
path | 该Cookie的使用路径。指定 cookie 在哪个路径(路由)下生效,如果设置为 /abc,则只有 /abc 下的路由可以访问到该 cookie,如果设置为“/”(默认),则本域名下的路由都可以访问该Cookie。注意最后一个字符必须为“/”。 |
maxAge | 该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1 |
expires | 过期时间,在设置的某个时间点后该 cookie 就会失效。 一般浏览器的 cookie 都是默认储存的,当关闭浏览器结束这个会话的时候,这个 cookie 也就会被删除 |
secure | 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。 当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效。 |
httpOnly | 如果给某个 cookie 设置了 httpOnly 属性,则无法通过 JS 脚本 读取到该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,所以只是在一定程度上可以防止 XSS 攻击,不是绝对的安全 |
comment | 该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明 |
Cookie的有效期是Cookie的一个重要属性。Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。
如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。
// 新建Cookie Cookie cookie = new Cookie("demo","democookie"); // 设置生命周期为MAX_VALUE cookie.setMaxAge(Integer.MAX_VALUE); // 输出到客户端 response.addCookie(cookie); 复制代码
如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。
maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。
如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除。
Cookie cookie = new Cookie("username","helloweenvsfei"); // 设置生命周期为0 cookie.setMaxAge(0); response.addCookie(cookie); 复制代码
从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。
response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。
如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。
2.2 什么是 Session
除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。session 是基于Cookie实现的,Session存储在服务器端,sessionId 会被存储到客户端的cookie 中。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
前面说到Cookie机制是通过检查客户身上的“通行证”来确定客户身份,而Session机制是通过检查服务器上的客户档案来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
session 认证流程:
用户第一次请求服务端的时候,服务器根据用户提交的相关信息,创建对应的 Session,请求返回时将此 Session 的唯一标识信息 SessionID 返回给客户端。客户端接收到服务端返回的 SessionID 后,会将此SessionID 存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。
用户第二次访问服务端的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
所以SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
Session的方法:
Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象,所有该客户的状态信息都保存在这个Session对象里。HttpSession提供了各种方法,常用的如下:
方法名 | 描述 |
String getAttribute(String attribute) | 获取session的属性 |
void setAttribute(String attribute, Object value) | 设置Session的属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大 |
String getId() | Session的ID。该ID由服务器自动创建,不会重复 |
long getCreationTime() | Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get CreationTime()) |
void invalidate() | 使该Session失效 |
int getMaxInactiveInterval() | Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效 |
void setMaxInactiveInterval(int second) | 设置Session的超时时间。单位为秒 |
void removeAttribute(String attribute) | 移除Session属性 |
Enumeration getAttributeNames() | 返回Session中存在的属性名 |
// 获取Session对象 HttpSession session = request.getSession(); // 设置Session中的属性 :登录时间 session.setAttribute("loginTime", new Date()); // 获取Session登录时间属性 out.println((Date)session.getAttribute("loginTime")); 复制代码
Session的生命周期:
Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。
Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session active了一次。
Session的有效期:
由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。
Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。
Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。
2.3 Cookie 和 Session 的区别
- 安全性: Session 比 Cookie 安全,因为Session 是存储在服务器端的,而Cookie 是存储在客户端的,所以可以分析存放在本地的Cookie 并进行Cookie 欺骗考虑到安全应当使用Session,但是可以对cookie进行加密 (这需要在后台的逻辑代码进行加密)。
- 存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
- 有效期不同: Cookie 可预先设置为长时间或永久保持,比如我们经常使用的记住密码功能就是使用永久cookie写在客户端电脑,下次登录时,自动将cookie信息附加发送给服务端,Session 一般有效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
- 存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。
使用 cookie 时需要考虑的问题:
- 因为存储在客户端,容易被客户端篡改,使用前需要验证合法性
- 不要存储敏感数据,比如用户密码,账户余额
- 使用 httpOnly 在一定程度上提高安全性
- 尽量减少 cookie 的体积,能存储的数据量不能超过 4kb
- 设置正确的 domain 和 path,减少数据传输
- cookie 无法跨域
- 一个浏览器针对一个网站最多存 20 个Cookie,浏览器一般只允许存放 300 个Cookie
- 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
使用 session 时需要考虑的问题:
- 将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
- 当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。
- 当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。
- sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现
- 移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token
所以cookie和session各自有不同的应用场景和优缺点,我们应该根据实际的应用场景来选择合适的。