FastAPI Cookie 与 Session 详解

举报
时光不写 发表于 2026/08/03 18:04:24 2026/08/03
【摘要】 在 Web 开发中,HTTP 是无状态协议——服务端默认不会记住客户端的上一次请求。Cookie 和 Session 正是为了解决"记住用户"这一需求而生的。FastAPI 基于 Starlette,提供了便捷的 Cookie 操作接口和 Session 中间件支持。本文系统讲解从 Cookie 基础到 Session 实战的全链路知识。 Cookie 的基本概念Cookie 是服务端通过 ...

在 Web 开发中,HTTP 是无状态协议——服务端默认不会记住客户端的上一次请求。CookieSession 正是为了解决"记住用户"这一需求而生的。FastAPI 基于 Starlette,提供了便捷的 Cookie 操作接口和 Session 中间件支持。本文系统讲解从 Cookie 基础到 Session 实战的全链路知识。

Cookie 的基本概念

Cookie 是服务端通过 Set-Cookie 响应头写入浏览器的一小段文本数据(通常不超过 4KB)。浏览器收到后会自动保存,并在后续请求中通过 Cookie 请求头自动回传。一个典型的 Cookie 工作流程:

客户端请求 → 服务端 Set-Cookie: user_id=abc → 浏览器保存
后续请求 → 浏览器自动带 Cookie: user_id=abc → 服务端识别用户

读取 Cookie

FastAPI 读取 Cookie 极其简单——直接将参数名声明为 Cookie 类型即可:

from fastapi import FastAPI, Cookie
from typing import Optional

app = FastAPI()


@app.get("/profile")
async def get_profile(user_token: Optional[str] = Cookie(None)):
    if user_token is None:
        return {"message": "未登录"}
    return {"user_token": user_token, "message": "欢迎回来"}

访问 http://localhost:8000/profile ——如果浏览器没有名为 user_token 的 Cookie,user_token 就是 None,返回"未登录"。

如果 Cookie 名和 Python 变量名不同(比如 Cookie 名带横线),可以用 alias 参数:

@app.get("/items/")
async def read_items(
    session_id: Optional[str] = Cookie(None, alias="session-id")
):
    return {"session_id": session_id}

设置 Cookie

设置 Cookie 需要操作 Response 对象。FastAPI 提供了 Response 参数注入,调用 set_cookie() 即可:

from fastapi import FastAPI, Response
from datetime import datetime, timedelta

app = FastAPI()


@app.post("/login")
async def login(username: str, response: Response):
    # 登录逻辑(此处简化)
    token = f"token_{username}_{datetime.now().timestamp()}"

    # 设置 Cookie
    response.set_cookie(
        key="user_token",
        value=token,
        max_age=3600,          # 有效期,单位秒
        httponly=True,          # 禁止 JS 读取,防 XSS
        secure=True,            # 仅 HTTPS 传输
        samesite="lax",         # 防 CSRF
        path="/",               # Cookie 生效路径
    )
    return {"message": "登录成功"}

set_cookie 参数详解

参数 说明 推荐值
key Cookie 名称 自定义
value Cookie 值(字符串)
max_age 过期时间(秒),优先级高于 expires 按需
expires 具体的过期 datetime
path 生效路径,/ 表示全站 /
domain 生效域名,默认当前域
secure 仅 HTTPS 传输 True
httponly 禁止 JavaScript 访问 True
samesite 跨站请求控制:lax / strict / none lax

安全提示:生产环境务必开启 httponlysecuresamesitehttponly 能防止 XSS 窃取 Cookie,samesite=lax 能防御大部分 CSRF 攻击。

删除 Cookie

删除 Cookie 的本质是设置一个立即过期的同名 Cookie:

@app.post("/logout")
async def logout(response: Response):
    response.delete_cookie(
        key="user_token",
        path="/",
        secure=True,
        httponly=True,
        samesite="lax",
    )
    return {"message": "已退出登录"}

delete_cookie() 内部会设置 max_age=0expires=0,浏览器收到后就会删除该 Cookie。注意删除时的 pathdomain 等参数必须和设置时一致,否则浏览器不会删除。

自定义 Response 中操作 Cookie

如果你的路由函数返回的不是简单字典,而是自定义 Response,同样可以操作 Cookie:

from fastapi.responses import JSONResponse


@app.post("/custom-login")
async def custom_login(username: str):
    token = f"custom_token_{username}"

    content = {"message": "登录成功"}
    response = JSONResponse(content=content)
    response.set_cookie(
        key="user_token",
        value=token,
        max_age=3600,
        httponly=True,
        secure=True,
        samesite="lax",
    )
    return response

Session 的实现

Session 本质上是"存储在服务端的用户数据",客户端只持有一个 Session ID(通常放在 Cookie 中)。FastAPI 没有内置 Session 机制,但 Starlette 提供了 SessionMiddleware,开箱即用。

安装与配置

SessionMiddleware 需要 itsdangerous 库用于签名:

pip install itsdangerous

在 FastAPI 应用中添加 Session 中间件:

from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware

app = FastAPI()

# 添加 Session 中间件
app.add_middleware(
    SessionMiddleware,
    secret_key="your-secret-key-change-in-production",
    session_cookie="session_id",       # Cookie 名称,默认 "session"
    max_age=3600,                       # 过期秒数,默认 14 天
    same_site="lax",
    https_only=False,                   # 开发环境 False,生产环境 True
)

@app.get("/set-session")
async def set_session(request: Request):
    request.session["username"] = "张三"
    request.session["role"] = "admin"
    return {"message": "Session 已设置"}


@app.get("/get-session")
async def get_session(request: Request):
    username = request.session.get("username", "游客")
    role = request.session.get("role", "anonymous")
    return {"username": username, "role": role}


@app.get("/clear-session")
async def clear_session(request: Request):
    request.session.clear()
    return {"message": "Session 已清除"}

SessionMiddleware 工作原理

  1. 读取请求 Cookie 中名为 session(或自定义名)的值
  2. secret_key 验签并解密,还原为 Python 字典挂载到 request.session
  3. 路由函数可以像操作普通字典一样读写 request.session
  4. 响应时,中间件将 request.session 序列化、签名后写回 Cookie

核心要点:Session 数据是加密签名后存在 Cookie 中的(客户端 Session),而不是存在服务端内存或数据库。优点是无需服务端存储,天然支持分布式;缺点是 Cookie 有 4KB 大小限制,且不应存放敏感数据(加密不等于安全,密钥泄露即全部暴露)。

实操:Cookie + Session 实现登录态

下面是一个完整的登录验证示例,展示 Cookie 和 Session 如何配合使用:

from fastapi import FastAPI, Request, Response, HTTPException, Cookie
from starlette.middleware.sessions import SessionMiddleware
from typing import Optional

app = FastAPI()

app.add_middleware(
    SessionMiddleware,
    secret_key="super-secret-key-2026",
    session_cookie="sid",
    max_age=86400,  # 24 小时
    same_site="lax",
)


# 模拟的用户数据库
fake_users = {"admin": "password123"}


@app.post("/api/login")
async def api_login(
    request: Request,
    response: Response,
    username: str,
    password: str,
):
    """用户登录接口"""
    if username not in fake_users or fake_users[username] != password:
        raise HTTPException(status_code=401, detail="用户名或密码错误")

    # 将用户信息存入 Session
    request.session["user"] = username
    request.session["login_time"] = "2026-07-19"

    # 同时设置一个独立的持久化 Cookie(比如"记住我"功能)
    response.set_cookie(
        key="remember_me",
        value="1",
        max_age=86400 * 30,  # 30 天
        httponly=True,
        secure=True,
        samesite="lax",
    )
    return {"message": f"欢迎 {username}!登录成功"}


@app.get("/api/me")
async def get_current_user(request: Request):
    """获取当前登录用户信息"""
    user = request.session.get("user")
    if user is None:
        raise HTTPException(status_code=401, detail="请先登录")
    return {
        "username": user,
        "login_time": request.session.get("login_time"),
    }


@app.post("/api/logout")
async def api_logout(request: Request, response: Response):
    """用户退出登录"""
    request.session.clear()  # 清除 Session
    response.delete_cookie("remember_me")  # 清除记住我 Cookie
    return {"message": "已退出登录"}

测试流程:POST /api/login → 浏览器自动保存 Cookie → GET /api/me 返回用户信息 → POST /api/logout 清除所有状态。

Cookie vs Session 对比

维度 Cookie Session (Starlette)
数据存储位置 客户端浏览器 Cookie 中(加密签名)
容量限制 每个 Cookie 约 4KB 受 Cookie 总大小限制
安全性 明文,可被用户篡改 加密签名,篡改可检测
过期控制 max_age / expires max_age(中间件参数)
适用场景 记住偏好、跟踪标识 登录态、用户数据

小结

Cookie 和 Session 是 Web 开发中管理用户状态的两大基石:

  • Cookie 适合存放轻量、非敏感的客户端标识,通过 set_cookie() 设置、Cookie() 读取
  • Session 通过 SessionMiddleware 实现,数据加密签名存储在 Cookie 中,适合存放登录态等需要防篡改的信息
  • 安全是底线:生产环境务必启用 httponlysecuresamesite,且 secret_key 绝对不可泄露

掌握了 Cookie 和 Session,你就能构建出有"记忆"的 Web 应用——记住用户偏好、维护登录状态、实现购物车功能,全都离不开它们。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。