前言
前一段时间开发项目时,用到了Next框架,个人感觉这个框架还是很厉害的。大大减少了工作量。就用保持登录状态这个例子来说。
对于传统的开发来说:
后端需要,使用密钥和加密算法生成Token;拦截所有需要身份验证的受保护路由的请求;从 Authorization 请求头中获取Token并进行验证。
前端需要,发送登录请求到后端的登录接口;接收后端登录成功的响应,将Token存入Local Storage中;封装请求在每个请求的 Authorization 请求头中添加Token。
这是一套标准的流程,当然还有一种是将Token放入Cookie中,看具体项目的做法。工作量还是有点多,在Next框架中使用next-auth,可以很轻松的实现我们的目的,差不多省了个后端的工作量。
正文
如果对Session、Cookie、Token不是很清楚的话,可以先往下面看介绍Session、Cookie、Token,然后再回来看
next-auth是如何保持登录状态的
那么next-auth是如何保存登录状态的呢。next-auth在用户成功登录后,会创建一个 Token,然后会将这个 Token 存储在客户端的 Cookie 中(默认情况下,Cookie 的名称是 next-auth.session-token)。从下面两幅图就很容易看出来他的实现方式是上面说的另一种实现方式,将Token放入Cookie中。


next-auth的session指的是什么
在第一次使用next-auth时,我就感到奇怪都使用JWT了,为什么还是用session?后来经过了解才明白这里的 session 指的是 next-auth在客户端维护的用户会话状态(用户当前的登录状态和用户信息),而不是传统的服务器端 Session 存储。
session 回调函数的作用是将JWT中的信息提取出来,并构建成 session 对象,这个 session 对象最终会通过 useSession hook 在客户端组件中访问,也可以通过getServerSession在服务器端获取 session。
为什么需要 session
- 方便访问用户信息:
useSessionhook 提供了一个方便的方式在客户端组件中获取当前用户的登录状态和用户信息。无需像传统做法那样,通过请求来获取用户的信息。 - 状态管理:
next-auth需要在客户端维护一个状态,知道用户是否已登录,以及用户的基本信息。useSessionhook 提供的status字段(例如loading,authenticated,unauthenticated) 可以让我们判断用户当前的状态。 - 其他: 比如路由保护和中间件中都需要判断用户的状态。
介绍Session、Cookie、Token
我们都知道HTTP 被称为是无状态的(stateless) ,意味着服务器不会记住客户端之间的任何交互信息。但是在实际中我们却又想让服务器知道我们是谁,这样才能对相应的用户做处理。为了实现这个目的就有了Cookie、Session、Token来实现在客户端和服务器端之间传递和管理状态信息。
我们用保持登录状态的来举例说明
Cookie
HTTP Cookie(通常简称 Cookie)是服务器发送到用户浏览的一小段文本数据。浏览器会将这段数据存储 起来,并在后续 对相同域名 (或符合特定规则的域名和路径)的请求中,通过 HTTP 请求头 自动携带 发送给服务器。本质就是存储在浏览器的数据。
Cookie 的工作原理:
- 服务器设置
Cookie
当用户第一次登录时,服务器可以在HTTP响应头中使用 Set-Cookie 字段来向用户的浏览器发送一个或多个 Cookie。
- 浏览器存储
Cookie:
用户的浏览器接收到包含 Set-Cookie 头的 HTTP 响应后,会解析这些指令,并将 Cookie 存储在本地。浏览器会根据Cookie的属性(例如 Domain、Path、Expires/Max-Age 等)来决定何时以及向哪些服务器发送这些 Cookie。
- 浏览器发送
Cookie:
当用户在之后访问相同域名 (或符合 Domain 属性)下的符合 Path 属性 的任何资源时,浏览器会在 HTTP 请求头中使用 Cookie 字段,将所有相关的 Cookie 一起发送给服务器。
- 服务器接收和使用
Cookie:
服务器接收到包含 Cookie 头的 HTTP 请求后,可以解析这个字段,获取之前设置的 Cookie 值。服务器可以根据 Cookie 中存储的信息来识别用户。
但是Cookie 是存储在用户的浏览器中,不能保证数据安全,也永远不要在 Cookie 中存储敏感信息,也是这个问题才有了Session。
Session
对于Cookie来说,Session敏感数据存储在服务器端,更加安全;Session 可以存储更大量的数据。所以开始使用Session,但是通常Session都依赖 Cookie,这是为什么,下面会讲。
Session 的工作原理:
-
用户首次请求: 当用户第一次登录时,服务器会创建一个与该用户关联的
Session 对象。这个Session 对象在服务器的内存、文件系统、数据库或其他存储介质中创建并存储。 -
生成
Session ID: 服务器会为这个新创建的Session 对象生成一个唯一的Session ID。 -
发送
Session ID给客户端: 服务器需要将这个Session ID传递给用户的浏览器,以便在后续的请求中能够识别出是同一个会话。最常用的方式是通过设置一个Session Cookie。这个Cookie通常只包含Session ID。 -
客户端发送
Session ID: 在用户后续对同一域名发起请求时,浏览器会自动将之前存储的Session Cookie(包含Session ID)通过HTTP请求头中的Cookie字段发送给服务器。 -
服务器识别
Session: 服务器接收到请求后,会读取请求头中的Cookie字段,获取Session ID。然后,服务器会根据这个Session ID查找之前存储的对应的Session 对象。 -
维护用户状态: 通过找到对应的
Session 对象,服务器可以访问和修改与该用户会话相关的数据。这些数据可以包括用户的登录状态等。 -
Session过期和销毁:Session在服务器端不会永久存在。它们通常会因为以下原因过期或被销毁:- 空闲超时: 如果用户在一段时间内没有活动(例如没有发送任何请求),服务器会认为该会话已过期。
- 绝对超时: 会话可能被设置为在一定时间后强制过期,无论用户是否有活动。
- 用户主动注销: 当用户点击"注销"等按钮时,服务器端的代码会显式地销毁当前的
Session。 - 会话 Cookie 过期: 如果
Session ID存储在会话Cookie中,当浏览器关闭时,Cookie会失效,服务器端的Session也可能被标记为过期等待回收。 - 服务器垃圾回收: 服务器通常会有垃圾回收机制,定期清理过期的
Session数据,以释放服务器资源。
但是,由于每个活跃的 Session 都会占用服务器的存储空间(内存、磁盘等)。在高并发场景下,大量的 Session 可能会消耗大量服务器资源。在多台服务器组成的分布式系统中,需要考虑如何共享 Session 数据,以确保用户的会话在不同的服务器之间保持一致。也是因为这些原因,后面才有了JWT,也就是常常说的Token。
Token
Token与传统的基于 Session 和Cookie的身份验证方式不同,基于 Token 的身份验证通常是无状态的,这意味着服务器不需要为每个用户的会话维护持久化的状态信息。
Token 的工作原理:
- 用户登录:客户端向服务器发送登录凭据(例如用户名和密码)。
- 服务器验证:服务器验证凭据的有效性。
- 颁发
Token: 如果验证成功,服务器创建一个Token,其中包含用户的身份信息和可选的过期时间等声明,并使用密钥对其进行签名。 - 返回
Token:服务器将生成的Token返回给客户端。 - 客户端存储
Token:客户端将收到的Token安全地存储起来(例如在Cookie中或Local Storage中)。 - 携带
Token请求:在后续请求时,客户端会将Token通过HTTP请求头(通常是Authorization: Bearer <Token>) 或Cookie发送给服务器。 - 服务器验证
Token: 服务器接收到请求后,提取Token,使用相同的密钥验证其签名和有效性(例如是否过期)。 - 授权访问: 如果
Token验证通过,服务器可以信任Token中包含的用户信息,并允许客户端访问相应的资源。
经历长期的发展,现在基本都是使用Token来保持登录状态的。
结语
谢谢大家观看!!!