FastAPI生命周期状态存储变量的机制及数据库客户端存储安全性咨询
FastAPI生命周期状态存储变量的机制及数据库客户端存储安全性咨询
我有一个用Motor连接MongoDB的FastAPI应用,我用
lifespan在应用启动时打开MongoDB连接,关闭时关闭连接:from contextlib import asynccontextmanager from fastapi import FastAPI, Request from motor.motor_asyncio import AsyncIOMotorClient @asynccontextmanager async def lifespan(app): mongo_client = AsyncIOMotorClient("mongo://") mongo_db = mongo_client["mydb"] yield {"db": mongo_db} mongo_client.close() app = FastAPI(lifespan=lifespan) @app.get("/") async def root(request: Request): return request.state.db在Starlette的旧版本中,这个变量是存在
app.state里的,但现在要存在request.state中。这种必须从Request对象里获取db变量的方式让我觉得数据库客户端会跟着每个请求走,可能会面临来自客户端的漏洞风险。这个状态变量到底是怎么运作的?它真的会跟着每个请求传输吗?把数据库客户端存在这里是否安全?
别担心,你完全多虑啦!咱们来拆解清楚这背后的逻辑:
1. 状态变量的实际运作方式
- 首先,
lifespan里yield出来的字典,FastAPI/Starlette并不会把它真的“附着”在每个请求里传输。它只是在请求处理的过程中,把这个字典里的内容临时关联到当前请求的request.state上——注意,这只是服务器端的内存关联,和客户端没有半毛钱关系,不会通过网络把数据库客户端传给用户的。 - 你的
mongo_client和mongo_db都是在服务器启动时创建的单例对象(整个应用生命周期内只有一个实例),request.state.db只是指向这个单例的一个引用而已,不是每次请求都新建一个客户端,也不会把客户端的信息暴露给外部。
2. 存储数据库客户端的安全性
- 绝对安全!
request.state是服务器端请求上下文里的变量,客户端完全访问不到它——用户能拿到的只有你在接口里返回的内容,比如你现在的root接口返回request.state.db,其实返回的是这个对象的字符串表示(比如<AsyncIOMotorDatabase(mydb)>),但这只是个标识,没有任何敏感信息,也没法让用户通过这个操作数据库。 - 对比旧版本的
app.state,其实本质是一样的:都是在服务器端存储全局对象,只是现在通过lifespan的yield机制,把全局对象注入到请求上下文里,方便在路由函数里访问而已,安全性没有任何变化。
3. 额外小提醒
- 不要在接口里直接返回
request.state.db哦(就像你示例里那样),虽然不会有安全问题,但没什么实际意义,应该返回从数据库查询到的具体数据。 - 你的
mongo_client是全局单例,整个应用复用这一个连接池,这是数据库连接的最佳实践,比每次请求新建客户端高效多了。
备注:内容来源于stack exchange,提问作者Ravshan S.K.
相关产品推荐
相关产品推荐

