我正在开发一个无状态 FastAPI 应用程序。身份验证通过 Google OAuth(
openid
电子邮件配置文件范围)进行处理。我正在使用
Authlib
,它使用
SessionMiddleware
(
request.session
) 来存储临时代码和状态。现在,我想使用令牌限制对某些端点的访问。为此,我使用从 Google 获得的 id 令牌中的用户电子邮件创建 JWT,将其存储在客户端的 cookie 中,并在每次向受保护端点发出请求时验证它。它正是我想要的。
自从我使用
SessionMiddleware
无论如何我想到了一种不同的方法。我可以将用户电子邮件存储在来自
SessionMiddleware
的会话中。它会自动创建一个会话 cookie 并将其存储在客户端。为了限制访问,我可以从会话中获取用户。 Starlette 在后台自动验证 cookie。我已经测试过它,它也工作得很好,并为我节省了很多 JWT 代码。
我的问题是
:是否
SessionMiddleware
将会话数据存储在客户端的 cookie 中,或者只是对 ID 进行编码并会话数据保存在服务器上?
根据我的研究和自己的实现,我得到了不同的答案。如果我重新启动应用程序,我仍然可以使用令牌访问会话数据。这暗示整个过程是无状态的。如果我检查 Starlette 的源代码,我还认为会话数据直接编码在会话 cookie 中。但我在其他地方都看到人们说
SessionMiddleware
有状态运行。
Starlette 的
SessionMiddleware
默认将会话数据存储在
客户端
的 cookie 中。
让我们来分解一下它的工作原理:
-
客户端发起请求:
当客户端首次向服务器发起请求时,
SessionMiddleware
会生成一个唯一的会话 ID 并将其与一个空的会话字典关联。 -
数据加密和存储:
SessionMiddleware
使用你提供的密钥对这个会话字典进行加密和签名,然后将其存储在名为session
的 cookie 中,并将其发送给客户端。 -
后续请求:
客户端在后续请求中会自动带上这个
session
cookie。 -
服务器端解密和验证:
SessionMiddleware
收到请求后,会从 cookie 中提取会话数据,使用密钥进行解密和验证,并将解密后的数据存储在request.session
中。 -
修改和保存:
你可以通过
request.session
对会话数据进行修改,SessionMiddleware
会在响应结束时自动将更新后的会话数据加密并存储回 cookie 中。
关于你观察到的现象:
- 重启应用后仍可访问: 这是因为会话数据存储在客户端的 cookie 中,而不是服务器内存中。只要 cookie 没有过期或被删除,客户端就可以继续使用它访问会话数据。
- Starlette 源码: Starlette 的源代码确实显示会话数据直接编码在 cookie 中。
关于“有状态”的误解:
有些人可能认为
SessionMiddleware
是“有状态”的,因为他们将“会话”的概念与服务器端的状态联系在一起。实际上,
SessionMiddleware
本身是无状态的,因为它不依赖服务器存储任何会话数据。所有会话数据都存储在客户端,服务器只是负责加密、解密和验证。
总结:
SessionMiddleware
提供了一种简单易用的方式来管理客户端会话,它默认使用 cookie 来存储会话数据,因此你的应用程序仍然是无状态的。 但是,需要注意的是,将敏感信息存储在 cookie 中存在安全风险,因此建议仅存储非敏感数据,并使用 HTTPS 来保护 cookie 的传输安全。