Cookie、Session、Token与前端存储

2,820 阅读11分钟

Cookie、Session

Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下一次向同一服务器发起请求时被携带并发送到服务器上

Session代表着服务器和客户端一次会话的过程,Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者Session超时失效时,会话结束

Cookie 和 Session 有什么不同?

  • 作用范围不同:Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同:Cookie 只能保存 ASCII,Session 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等。
  • 有效期不同:Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
  • 隐私策略不同:Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
  • 存储大小不同, 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。

Cookie

cookie机制

  • Session Cookie(会话期 Cookie):会话期 Cookie 是最简单的Cookie,它不需要指定过期时间(Expires)或者有效期(Max-Age),它仅在会话期内有效,浏览器关闭之后它会被自动删除。
  • Permanent Cookie(持久性 Cookie):与会话期 Cookie 不同的是,持久性 Cookie 可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。

cookie的原理

第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会在响应头里面添加一个Set-Cookie选项,将cookie放入到响应请求中,在浏览器第二次发请求的时候,会通过Cookie请求头部将Cookie信息发送给服务器,服务端会辨别用户身份,另外,Cookie的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。

cookie的安全策略

属性作用
value如果用于保存用户状态,应该将值加密,不能使用明文的用户标识
http-only告诉浏览器此Cookie只能靠浏览器Http协议传输,禁止其他方式访问,不能通过JS访问Cookie,减少XSS攻击
secure只能在协议为https的请求中携带,使用 HTTPS 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。
same-site规定浏览器不能在跨域请求中携带cookie,减少CSRF攻(但存在一定的浏览器兼容性)
Max-Age设置cookie的过期时间,单位为秒
Domain指定了Cookie所属的域名

SameSite

  • Strict 仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
  • Lax 允许部分第三方请求携带 Cookie
  • None 无论是否跨站都会发送 Cookie

应用场景:

  • 会话状态管理(如用户登录状态,购物车,游戏分数或者其他需要记录的信息)
  • 个性化设置
  • 浏览器行为跟踪

Session

session机制

当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid,如果包含,则根据id返回对应的session,如果没有,服务器端会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则需要重写URL

应用场景: 用于保存每个用户的专用信息,通过SessionID来区分不同的用户

  • 网上商城中的购物车
  • 保存用户的登录信息
  • 将某些公用数据保存在session中
  • 防止用户非法登录

分别的缺点:

cookie:

  • 大小受限
  • 用户可以禁用cookie
  • 安全性较低
  • 有些状态不可能保存在客户端
  • 每次访问都要传送cookie给服务器,浪费带宽
  • cookie数据有路径的概念,可以限制cookie只属于某个路径下(每个 Cookie 都会有与之关联的域,这个域的范围一般通过 donmain 属性指定。如果 Cookie 的域和页面的域相同,那么我们称这个 Cookie 为第一方 Cookie(first-party cookie),如果 Cookie 的域和页面的域不同,则称之为第三方 Cookie(third-party cookie)。一个页面包含图片或存放在其他域上的资源(如图片)时,第一方的 Cookie 也只会发送给设置它们的服务器。)

session:

  • session保存的东西越多,就越占用服务器内存,对于在线用户较多的网站,服务器的内存压力会比较大
  • 依赖于cookie,如果禁用cookie,则要使用URL重写,不安全 -创建session变量有很大的随意性,可以随时调用,不需要开发者做精确的处理,所以过度使用session变量会导致代码不可读且不好维护

如果浏览器禁用了Cookie,如何保障整个机制正常运转

  • 第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接 xxx?SessionID=123456...。
  • 第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。 Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和 Session 的使用机制比较类似。

分布式Session

为了支撑更大的流量,后端往往需要多台服务器共同支撑前端的用户请求,那么如果用户在服务器A登录了,第二次请求如果在服务器B就会出现登录失效的问题

分布式 Session 一般会有以下几种解决方案:

  • Nginx ip_hash 策略,服务端使用 Nginx 代理,每个请求按访问 IP 的 hash 分配,这样来自同一 IP 固定访问一个后台服务器,避免了在服务器 A 创建 Session,第二次分发到服务器 B 的现象。

  • Session 复制,任何一个服务器上的 Session 发生改变(增删改),该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点。

  • 共享 Session,服务端无状态话,将用户的 Session 等信息使用缓存中间件来统一管理,保障分发到每一个服务器的响应结果都一致。

Token

Token是在服务端将用户信息经过Base64Url编码过后传给在客户端,每次用户请求的时候都会带上这一段信息,因此服务端拿到此信息进行解密后就知道此用户是谁了,这个方法叫做JWT(Json Web Token)。

token.png

  1. 用户通过用户名和密码发送请求
  2. 程序验证
  3. 程序返回一个签名的token给客户端
  4. 客户端储存token, 并且每次用每次发送请求
  5. 服务端验证Token并返回数据

Token相比较于Session的优点在于,当后端系统有多台时,由于是客户端访问时直接带着数据,因此无需做共享数据的操作。

优点:

  1. 简洁:可以通过URL,POST参数或者是在HTTP头参数发送,因为数据量小,传输速度也很快
  2. 自包含:由于串包含了用户所需要的信息,避免了多次查询数据库
  3. 因为Token是以Json的形式保存在客户端的,所以JWT是跨语言的
  4. 不需要在服务端保存会话信息,特别适用于分布式微服务

JWT(JSON Web Token)

JSON Web Tokens由dot(.)分隔的三个部分组成,它们是:

  1. Header(头部)
  2. Payload(负载)
  3. Signature(签名)

因此,JWT通常如下展示:

xxxxx.yyyyy.zzzz

Header(头部)

Header 是一个 JSON 对象

{
  "alg": "HS256", // 表示签名的算法,默认是 HMAC SHA256(写成 HS256)
  "typ": "JWT"  // 表示Token的类型,JWT 令牌统一写为JWT
}

Payload(负载)

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据

{
  // 7个官方字段
  "iss": "a.com", // issuer:签发人
  "exp": "1d", // expiration time: 过期时间
  "sub": "test", // subject: 主题
  "aud": "xxx", // audience: 受众
  "nbf": "xxx", // Not Before:生效时间
  "iat": "xxx", // Issued At: 签发时间
  "jti": "1111", // JWT ID:编号
  // 可以定义私有字段
  "name": "John Doe",
  "admin": true
}

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

Signature(签名)

Signature 是对前两部分的签名,防止数据被篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用Header里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

算出签名后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

JWT = Base64(Header) + "." + Base64(Payload) + "." + $Signature

如何保证安全

  • 发送JWT要使用HTTPS;不使用HTTPS发送的时候,JWT里不要写入秘密数据
  • JWT的payload中要设置expire时间

使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务端通信,都要带上这个JWT。你可以把它放在Cookie里面自动发送,但是这样不能跨域,所以更好的做法是放在HTTP请求的头信息 Authorization 字段里面。

Authorization: Bearer <token>

另一种做法是, 跨域的时候, JWT就放在POST请求的数据体里。

JWT的作用

JWT最开始的初衷是为了实现授权和身份认证作用的,可以实现无状态,分布式的Web应用授权。大致实现的流程如下

JWT.png

  1. 客户端需要携带用户名/密码等可证明身份的的内容去授权服务器获取JWT信息;
  2. 每次服务都携带该Token内容与Web服务器进行交互,由业务服务器来验证Token是否是授权发放的有效Token,来验证当前业务是否请求合法。

注意:不是每次请求都要申请一次Token,如果不是对于安全性要求的情况,不建议每次都申请,因为会增加业务耗时;比如只在登陆时申请,然后使用JWT的过期时间或其他手段来保证JWT的有效性;

WebStorage

localStorage和sessionStorage

  1. 生命周期 localS的生命周期是永久的,关闭页面或浏览器后,数据也不会消失,除非手动清除数据 sessionS的生命周期是仅在当前会话下有效,仅在当前的浏览器窗口下存在,关闭窗口则被销毁

  2. 存储大小:localStorage和sessionStorage的存储大小一般都是5M

  3. 存储位置:都保存在客户端,不与服务端进行交互

  4. 存储内容类型:都只能存储字符串类型,对于复杂的对象可以用JSON对象的stringify和parse来处理

  5. 应用场景: localS:常用于长期登录(判断用户是否已经登录),适合长期保存在本地的数据 sessionS:敏感账号一次性登录

特性cookielocalStoragesessionStorageindexDB
数据生命周期一般由服务器生成可以设置过期时间除非被清理,否则一直存在页面关闭后清理除非被清理,否则一直存在
数据存储大小4K5M5M无限
与服务端通信每次都会携带在header中,对于请求性能影响不参与不参与不参与

参考