Cookie、Session、JWT 的前世今生

一、Cookie

在互联网早期,HTTP 协议是无状态的,客户端每次访问,服务端和都不会保存任何会话信息。这可给网站带来了大麻烦,比如用户登录后浏览不同页面,网站无法识别这是同一个用户,购物车功能也难以实现。1994 年,网景公司的 Lou Montulli 为了解决用户网上购物的购物车历史记录问题,发明了 Cookie,从此开启了 Web 状态管理的新篇章。

当你访问一个网站,比如淘宝,服务器会在响应头中添加一个 Set - Cookie 字段,就像是给你递上一张小纸条,上面写着一些信息,比如你的用户 ID、会话标识符等。你的浏览器收到后,会把这张小纸条(Cookie)妥善保存在本地。之后,每次你再向淘宝服务器发起请求,浏览器都会自动把这张小纸条带上,服务器通过读取纸条上的信息,就能识别出你是谁,了解你的一些状态,比如是否已登录。

Cookie 是一段不超过 4KB 的小型文本数据,别看它小,五脏俱全。它由一个名称(Name)、一个值(Value)和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成。

  1. Name/Value:这是 Cookie 的核心,就像你的名字和你对应的身份信息。比如,Name 是 "user_id",Value 可能就是你的淘宝账号 ID。对于认证 Cookie,Value 值可能包括 Web 服务器所提供的访问令牌。

  2. Expires 属性:它决定了 Cookie 的 "寿命"。有会话性 Cookie 和持久性 Cookie 之分。Expires 属性缺省时,Cookie 是会话性的,仅保存在客户端内存中,一旦你关闭浏览器,它就消失了;持久性 Cookie 则会保存在你的硬盘中,直到它的生存期到了,或者你主动在网页中单击 "注销" 等按钮结束会话时才失效。

  3. Path 属性:它规定了 Web 站点上哪些目录可以访问该 Cookie。

  4. Domain 属性:指定了可以访问该 Cookie 的 Web 站点或域。Cookie 机制有点像一个不太严格的保安,允许一个子域可以设置或获取其父域的 Cookie。在实现单点登录方案时,这个特性很有用,但也带来了安全风险,比如攻击者可能借此发动会话定置攻击。所以,浏览器也做了一些限制,禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,降低被攻击的可能性。

  5. Secure 属性:指定是否使用 HTTPS 安全协议发送 Cookie。使用 HTTPS 安全协议,能保护 Cookie 在浏览器和 Web 服务器间传输过程中不被窃取和篡改。不过,由于兼容性问题,有些网站使用自签署的证书,浏览器检测到 SSL 证书无效时,不会立刻终止连接请求,而是提示安全风险信息,一些缺乏安全意识的用户可能会继续访问,这就给了攻击者可乘之机。

  6. HTTPOnly 属性:这是防止 Cookie 被跨站脚本攻击(XSS)窃取或篡改的一道防线。它用于防止客户端脚本通过 document.cookie 属性访问 Cookie。但它也不是万能的,一些浏览器可以阻止客户端脚本对 Cookie 的读操作,但允许写操作;大多数浏览器仍允许通过 XMLHTTP 对象读取 HTTP 响应中的 Set - Cookie 头。

Cookie的认过程:

  1. 客户端首次向服务器发送请求时

  2. 服务器生成包含用户标识信息的 Cookie,通过响应头传递给客户端,客户端存储该 Cookie。

  3. 后续客户端向服务器发送请求时,请求头会自动携带此 Cookie

  4. 服务器通过解析 Cookie 中的信息完成用户身份识别与认证。

  5. 若 Cookie 过期或被删除,客户端需重新获取 Cookie 以完成认证。

Cookie 的应用非常广泛。在会话管理方面,它可以用于跟踪用户的状态,比如在购物车应用中,记录你添加到购物车的商品;在用户认证中,存储你的登录状态和凭据,让你在浏览网站不同页面时无需重复登录;还能存储用户的偏好设置,如你在网站上设置的语言、主题等,下次访问时直接呈现你喜欢的样式。

然而,Cookie 的安全性较低。攻击者可以通过木马等恶意程序,或使用跨站脚本攻击等手段偷窃存放在用户硬盘或内存中的 Cookie。比如在不安全的局域网中被动监听网络通信,或者发动 DNS Pharming(域欺骗)攻击,将你对合法网站的访问请求重定向到恶意网站,从而窃取 Cookie。对于捕获到的认证 Cookie,攻击者可能会猜测其中的访问令牌,试图获取敏感信息,或者直接重放该 Cookie,假冒你的身份进行各种操作。

Session

虽然 Cookie 解决了一些 HTTP 无状态的问题,但由于它存储在客户端,存在安全隐患,而且大小有限制。于是,Session 应运而生,它就像是服务器端为每个用户建立的一份详细 "客户档案"。

当客户端浏览器第一次访问服务器时,服务器会为这个客户端创建一个 Session 对象,就像为新顾客建立一个专属档案。这个 Session 对象里可以存储各种与该用户相关的信息,比如用户登录状态、购物车信息、在应用中的操作记录等。同时,服务器会生成一个唯一的 Session ID,这个 ID 就像是档案编号,服务器把这个 Session ID 通过 Cookie 发送给客户端。之后,客户端每次请求服务器时,都会带上这个包含 Session ID 的 Cookie,服务器通过读取这个 Session ID,就能从众多 "客户档案" 中找到对应的 Session 对象,获取用户的状态信息。

  1. 服务器端存储:与 Cookie 存储在客户端不同,Session 数据存储在服务器上,这大大提高了数据的安全性,因为客户端无法直接修改服务器上的 Session 数据。

  2. 大小限制:一般来说,Session 没有像 Cookie 那样严格的 4KB 大小限制,它的大小主要受服务器内存限制。这意味着可以在 Session 中存储更复杂、更多的数据。

  3. 持久性:Session 会在一定时间内保存,这个时间通常由服务器的配置决定,比如设置为 30 分钟无操作则过期。相比之下,Cookie 的持久性可以通过设置 Expires 属性灵活调整,既可以是会话级别的,也可以是长期保存的。

  4. 安全性:由于数据保存在服务器端,Session 比 Cookie 更安全。但如果 Session ID 被泄露,攻击者同样可以通过这个 ID 访问对应的 Session,造成 Session 劫持攻击,所以在使用 Session 时,也要注意保护 Session ID 的安全。

  5. 跨域限制:Session 不受同源策略限制,它通过 Session ID 来识别和关联请求。不过,在跨域场景下,要实现 Session 共享可能会比较复杂,需要一些额外的配置和技术手段。

session的认证过程

  1. 客户端首次请求服务器

  2. 服务器创建 Session 对象存储用户数据,并生成唯一的 Session ID,将 Session ID 通过 Cookie 发送至客户端。

  3. 客户端后续请求会携带该 Session ID,服务器依据 Session ID 查找对应的 Session 对象,以此完成用户认证。

  4. 当 Session 超时或被销毁,客户端需重新建立 Session 以进行认证。

在实际应用中,Session 和 Cookie 通常是配合得非常默契的一对搭档。比如,用户登录成功后,服务器创建 Session 并生成 Session ID,将这个 Session ID 通过 Cookie 发送给客户端。当客户端再次请求时,带着包含 Session ID 的 Cookie,服务器根据 Session ID 找到对应的 Session,确认用户身份和状态。即使关闭了浏览器,下次打开浏览器访问网站时,如果 Cookie 中的 Session ID 还未过期,仍然可以通过它恢复会话状态。但这种配合使用也需要注意安全性问题,要防止 Session 劫持等攻击。因此也可以说SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

  1. 存储位置:Cookie 是存储在客户端的,就像你把重要信息揣在自己兜里;而 Session 是存储在服务器端的,相当于把信息交给了一个专门的管理员保管。

  2. 安全性:由于 Cookie 在客户端,容易被篡改和窃取,安全性相对较低;Session 在服务器端,客户端无法直接接触到数据,安全性更高,但 Session ID 通过 Cookie 传递时,Session ID 的安全也需要保障。

  3. 数据大小:Cookie 有严格的大小限制,一般不超过 4KB;Session 的数据大小主要受服务器内存限制,能存储更多更复杂的数据。

  4. 生命周期:Cookie 可以通过设置 Expires 属性来控制生命周期,既可以是会话级别的,也可以长期存在;Session 的生命周期通常由服务器设置,一般在用户一段时间不操作后自动失效。

  5. 服务器压力:Cookie 存储在客户端,不会增加服务器的存储压力;Session 存储在服务器,大量的 Session 会消耗服务器的内存资源,对服务器压力较大。

  6. 跨域支持:Cookie 在跨域时会受到诸多限制,不同域名之间共享 Cookie 比较困难;Session 本身是基于 Session ID 的,只要 Session ID 能传递过去,理论上可以跨域,但实际中由于 Cookie 的跨域限制,Session ID 的传递也会受到影响。

三、JWT

随着互联网的发展,特别是移动应用和分布式系统的兴起,传统的 Session 认证方式面临一些挑战。Session 认证需要在服务器端存储大量的会话状态信息,这对于分布式系统来说,服务器之间共享 Session 数据变得复杂,而且在移动端,Cookie 可能不太适用。于是,JWT(JSON Web Token)这种基于 Token 的认证方式逐渐流行起来。

  1. 用户在前端通过 Web 表单输入用户名和密码,然后以 POST 请求的方式发送到后端接口。

  2. 后端服务器收到请求后,核对用户名和密码。如果验证成功,服务器会将包含用户信息的数据作为 JWT 的 Payload(负载),再将 JWT Header(头部)和 Payload 分别进行 Base64 编码,然后使用一个密钥(Secret Key)对它们进行签名,最终形成一个 JWT Token,这个 Token 就像是一个加密的通行证,是一个类似 "lll.zzz.xxx" 的字符串。

  3. 服务器将生成的 JWT Token 返回给前端客户端。客户端可以把这个 Token 存储起来,比如放到本地存储或者 Cookie 中。

  4. 之后,客户端每次向服务器请求资源时,都需要在请求头(Header)或者 Cookie 中携带这个 JWT Token。服务器收到请求后,会验证这个 JWT Token 的签名是否有效,以及 Token 是否过期等。如果验证成功,就会根据 Token 中的用户信息,向客户端返回请求的数据。

JWT 由三部分组成:Header(头部)、Payload(负载)和 Signature(签名),它们之间用点号 "." 分隔。

  1. Header(头部) :通常包含两部分信息,一是令牌的类型,一般就是 JWT;二是所使用的签名算法,比如 HMAC SHA256 或 RSA 等。这部分内容经过 Base64 编码后,成为 JWT 的第一部分。

  2. Payload(负载) :这里面包含了声明(claims),也就是你想要传递的信息,比如用户 ID、用户名、用户角色、过期时间等。需要注意的是,Payload 中的信息虽然是经过编码的,但并不是完全加密的,任何人都可以解码查看,所以不要在里面存放过于敏感的信息,比如用户密码。这部分内容经过 Base64 编码后,成为 JWT 的第二部分。

  3. Signature(签名) :为了生成签名部分,需要使用编码后的 Header、编码后的 Payload、一个密钥(Secret Key)以及 Header 中指定的签名算法。签名的作用是保证 JWT 在传输过程中没有被篡改。如果有人试图修改 JWT 中的内容,那么签名验证就会失败。

与传统的 Session 认证方式相比,JWT 有很多优势。

  1. 跨语言、跨平台:因为 JWT Token 是以 JSON 加密形式保存在客户端的,所以它是跨语言的,原则上任何 Web 形式都支持,无论是在 PC 端的浏览器,还是在移动端的应用,都能很好地使用 JWT 进行认证。

  2. 无状态:JWT 机制在服务端不需要存储 Session 信息,因为 JWT 自身包含了所有登录用户的信息,这大大减轻了服务端的压力,特别适合分布式系统和微服务架构。

  3. 支持跨域访问:Cookie 存在跨域限制,而 JWT 由于通常放在请求头中,不需要依赖 Cookie,所以跨域后不会存在信息丢失问题,方便在不同域名的服务之间进行身份验证。

  4. 更适用 CDN:可以通过内容分发网络请求服务端的所有资料,因为 JWT 包含了足够的用户信息,CDN 可以直接根据 JWT 来提供相应的资源。

  5. 无需考虑 CSRF(跨站请求伪造) :由于不再依赖 Cookie,采用 JWT 认证方式不会发生 CSRF 攻击,也就无需专门考虑 CSRF 的防御措施。

不过,JWT 也不是绝对安全的。虽然 JWT 的哈希签名密钥存放在服务端,理论上只要服务器不被攻破,JWT 是安全的,但实际上仍然存在一些风险。比如,JWT 是在请求头中传递的,如果网络被劫持,攻击者有可能获取到 JWT。为了应对这种情况,推荐使用 HTTPS 来传输数据,增加安全性。另外,JWT 可以使用暴力穷举来破解,所以为了提高安全性,可以定期更换服务端的哈希签名密钥,就像定期更换门锁密码一样。

Session 和 Token(以 JWT 为例)的区别

  1. 存储位置:Session 数据存储在服务器端,服务器需要维护这些会话数据;而 JWT 是存储在客户端的,服务器不需要存储相关数据,这也是 JWT 无状态的体现。

  2. 认证流程:Session 认证中,客户端携带 Session ID,服务器通过 Session ID 找到对应的 Session 数据来完成认证;JWT 认证中,客户端携带 JWT,服务器对 JWT 进行验证签名等操作,无需查询服务器存储的信息就能完成认证。

  3. 扩展性:在分布式系统中,Session 需要解决不同服务器之间的 Session 共享问题,通常需要借助缓存中间件等;而 JWT 由于服务器无需存储状态,不同服务器之间可以直接使用 JWT 进行认证,扩展性更好。

  4. 过期处理:Session 可以在服务器端直接销毁,让用户立即下线;JWT 一旦生成,在有效期内是无法在服务器端直接废除的,除非更改密钥或者在服务器端维护一个黑名单,但这会增加复杂度。

  5. 数据包含:Session 中存储的数据只在服务器端可见;JWT 的 Payload 部分包含了一些用户信息,虽然经过编码,但可以被解码查看,所以不能存放敏感信息。

  6. 适用场景:Session 适合单体应用,对安全性要求较高且需要服务器能主动控制会话的场景;JWT 适合分布式系统、移动应用等场景,能减少服务器的存储压力和提高系统的扩展性。

作为前端开发者,理解它们的底层逻辑并非为了非此即彼的选择,而是要根据具体场景灵活搭配。比如用 Cookie 存储 Session ID 实现基础会话,用 JWT 处理跨域 API 调用,再结合 HTTPS 和令牌刷新策略加固安全防线。

相关推荐
uncleTom666几秒前
前端地图可视化的新宠儿:Cesium 地图封装实践
前端
lemonzoey2 分钟前
无缝集成 gemini-cli 的 vscode 插件:shenma
前端·人工智能
老家的回忆11 分钟前
jsPDF和html2canvas生成pdf,组件用的elementplus,亲测30多页,20s实现
前端·vue.js·pdf·html2canvas·jspdf
半点寒12W15 分钟前
uniapp全局状态管理实现方案
前端
Vertira16 分钟前
pdf 合并 python实现(已解决)
前端·python·pdf
PeterJXL1 小时前
Chrome 下载文件时总是提示“已阻止不安全的下载”的解决方案
前端·chrome·安全
hackchen1 小时前
从0到1解锁Element-Plus组件二次封装El-Dialog动态调用
前端·vue.js·elementui
君子宜耘心1 小时前
el-table虚拟列表封装
前端
黄瓜沾糖吃1 小时前
大佬们指点一下倒计时有什么问题吗?
前端·javascript
温轻舟1 小时前
3D词云图
前端·javascript·3d·交互·词云图·温轻舟