跳到主要内容

4.2-Session、Cookie、Token

Create by fall on 24 Nov 2021 Recently revised in 18 May 2024

因为浏览器无状态的特性,每一次请求都是全新的请求,服务器不知道浏览器的历史请求记录,Cookie 和 Session 就是为了弥补这些而出现的。

实际上来讲,Session 是特殊的 Cookie ,和 Cookie 一样通过 Cookie 字段返还给服务器,只不过 Session 是存储在内存中,不会写入磁盘,并且随着浏览器的关闭消失。

如果没有设置过期时间,就是 Session,如果有过期时间,Cookie 会设置到磁盘中,然后随着过期而删除。

Session

客户端向服务端请求时,服务端会为这次请求开辟一内存空间,这个内存空间便是 Session 对象,存储结构为 ConcurrentHashMap,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。

服务器如何判断是同一个会话呢?

服务器:第一次接到请求后,创建 Session 空间,生成 sessionid,并通过响应头的 Set-Cookie:JSESSIONID=XXXXXXX 命令,发送设置 Cookie 的响应。

客户端:收到该响应后,设置一个 JSESSIONID=XXXXXXX 信息,过期时间为浏览器会话结束。

好了,在之后的请求中,请求头都会携带 Cookie 信息,服务器通过请求头中的 Cookie,获取 JSESSIONID ,得到 sessionId。

浏览器关闭后,sessionStorage 中的数据就会被清空。

缺点

如果做了负载均衡,请求数量变化,会导致请求不同的服务器,每次切换服务器都会导致 session 对不上,从而失效。

HTTP 协议中,Cookie 包括 Web Cookie 和浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。浏览器会把这小块数据存储,并与下一个请求一起发送到服务器。

比如说:

Cookie: MONITOR_WEB_ID=90f5539a-7677-49e7-af6c-b783230bd146; _ga=GA1.2.296034742.1637304562;

一般来讲,就是之后的 get 请求,都会携带上 Cookie

Cookie 主要用于三个目的:

  • 会话管理:视频播放记录,游戏得分
  • 个性化设置:主题,个性偏好
  • 追踪:用户的行为

Cookie 曾经用于一般的客户端存储,曾经是客户端唯一的存储方法,但现在建议使用现代存储 API。因为 Cookie 会随每个请求一起发送,所以大量存储会降低网络性能。

Cookie 一般有两种,一种是 Session Cookies 另一种是 Persistent Cookies 。也就是 Cookie 不设置过期时间就是 Session,和设置过期时间就是 Persistent Cookies。

Session Cookie 在客户端关闭时 Cookie 会删除,除非它指定 ExpiresMax-Age 指令。

这里再复习一下 Expires 是 Data 对象,Max-age 是毫秒数,都放在响应头

Expires / Max-Age:"Mon, 21 Feb 2022 01:22:06 GMT"

浏览器可能会使用会话还原,使大多数 Cookie 保持先前状态,就像从未关闭浏览器。

哪怕 Cookie 是通过 HTTPS 进行加密的方式传输到服务器的,即使是安全的,也不应该将敏感的信息存储在 cookie 中。

HttpOnly

HttpOnly 是微软对 Cookie 做的扩展,该值指定 Cookie 是否可通过客户端脚本访问。设置为 true 可以防止被窃取,当然这是02年对 ie6 上做的特性,现在能否使用有待考证。

Cookie 的作用域

Domain 和 Path 定义了 Cookie 的作用域,Cookie 应该发送给那些 URL

上实例!

如果设置了 Domain = mozilla.org,那么该域名子域名也可以使用 xyz.mozilla.org

设置地址的话 Path = /root 表示该 root 目录下的地址都会匹配。

Token

token 一般用来判断用户是否登陆,包含的内部信息有:uid(用户唯一身份标识)、time(当前事件的时间戳)、sign(签名,token 的前几位以哈希算法压缩成一定长度的十六进制字符串)

token 可以存放在 Cookie 中,token 是否过期,由后端进行判断,后端用数据表明 token 失效,前端进行处理,重新登陆,然后再获取新的 token 进行赋值。

JSON Web Tokens

简称为 JWT、称为 JSON 令牌,安全地将信息作为 JSON 对象进行传输的一种形式。JWT 中存储的信息是经过数据签名(数据签名简单的理解就是,被认证的信息发送者的证明)

可以使用 HMAC 算法或使用 RSA/ECDSA 的公用/专用密钥对 JWT 进行签名。

可实现的功能:

  • 认证(Authorization):一旦用户登录,之后的每个请求都会包含 JWT 从而允许用户访问该令牌所允许的路由、服务、资源。单点登录是当今广泛使用 JWT 的一项功能,因为开销很小
  • 信息交换(Information Exchange):JWT 能够安全传输信息,使用 公钥/私钥 对 JWT 进行签名验证,签名是通过 headpayload 计算的,甚至可以验证是否遭到篡改。

JWT 的格式

xxxxxxx.yyyyyyy.zzzzzzzzz
↑ ↑ ↑
Header、Payload、Signature

Header 由两部分组成

  • 令牌的类型:JWT
  • 签名算法:HMAC SHA256 或者 RSA
{
"alg":"HS256",
"typ":"JWT"
}

指定类型和签名算法后,JSON 块会被 Base94URL 编码成 JWT 的第一部分

Payload

包含一个声明,可以是有关实体,和其他数据的声明。共有三种类型的声明:registered,public,private声明。

registered 声明,建议使用的预定义声明。

| iss(issuer)          | 签发人   |
| exp(expiration time) | 过期时间 |
| sub(subject) | 主题 |
| aud(audience) | 受众 |
| nbf(Not Before) | 生效时间 |
| iat(Issued At) | 签发时间 |
| jti(JWT ID) | 编号 |

public 声明

公共的声明,可以添加任何的信息,一般添加用户相关的信息或者其他业务需要的必要信息,但不建议添加敏感信息,该部分可以在客户端解密。

private 声明

自定义声明,同意她们的各方之间共享信息,和上面两者皆不同,比如下面的这个

{
"sub":"1115454766",
"name":"John Doe",
"admin":true
}

之后,Payload JSON块会被 Base64Url 编码形成 JWT 的第二个部分。

signature

一个签证信息,有三部分组成

  • header (base64后)
  • payload(base64后)
  • secret
比如我们需要 HMAC SHA256 算法进行签名
HMACSHA256(base64UrlEncode(header)+'.'+base64UrlEncode(payload),secret)

签名用于验证消息传递过程中是否有更改,并且对于使用私钥进行签名的令牌,还可以验证 JWT 的发送者的真实身份。

把两个点分割的三段数据组合在一起,就是一个完整的 JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

在该网站上,可以尝试编写一个 JWT https://jwt.io/#debugger-io

密码签名 JWT 具有加密签名,Session Cookie 没有

状态JSON 是无状态的,所以 JWT 是无状态的,因为声明被存储在客户端

身份认证可以在本地进行,而不是请求必须通过服务器数据库之类的未知,可以对用户进行多次身份验证,无需与站点或应用程序进行多次身份验证,因此也无需消耗大量资源。

可扩展性

Session Cookie 存储在服务器中,表明如果网站或者是应用很大时,将会消耗大量的资源。JWT 的无状态会节省相当多的服务器资源。

跨域认证

因为Session Cookie 认证在服务器端,且只能用在单个节点的域,或者它的子域有效,如果第三个节点访问时,就会被禁止,如果希望自己的站点和其他站点建立安全连接时,会成为一个问题。

JWT可以解决跨域认证,能供通过多个节点进行用户认证,也就是 跨域认证。

cookie 和 JWT 在原理上有区别的,如果使用 cookie 的话,实际上 cookie 里面存的是 sessionID,后端就需要有个地方存储 sessionID 和登录信息的映射,如果使用 JWT 的话,不需要这个地方来存映射,

JWT 对比 Cookie 就是用计算时间换存储空间。

单点登录

相同域名,不同子域名下的单点登录:在浏览器端,根据同源策略,不同子域名的cookie不能共享。所以设置SSO的域名为根域名。SSO登录验证后,子域名可以访问根域名的 cookie,即可完成校验。在服务器端,可以设置多个子域名session共享(Spring-session)

为什么要用单点登录,因为如果八个应用,就会有八个登录措施,八个权限验证,所以将登录单独作为一个站点进行使用,叫单点登录

参考文章

作者链接
程序员cxuanhttps://juejin.cn/post/6844904115080790023