引言
HTTP协议本身是无状态的,意味着它并不保留关于用户或会话的持久信息。在HTTP请求的往返过程中,服务器无法直接识别出是哪个具体用户发起的请求。为了解决这个问题,并在多个请求之间维持用户的身份和状态,引入了cookie这一机制。
Cookie通过在客户端(如浏览器)存储小段数据,并在后续的HTTP请求中将这些数据发送给服务器,从而间接地让HTTP请求信息中包含了用户的状态信息。这种方式允许服务器识别出是哪个用户发起的请求,并据此提供个性化的服务或保持会话的连续性。
本文主要讲述 在FastAPI 如何设置cookie与获取cookie信息,以及通过一个注册登陆案例进行讲解如何保存用户登陆状态。
简单使用Cookie参数
from datetime import datetime, timedelta
import uvicorn
from fastapi import FastAPI, Body, Cookie
from fastapi.responses import JSONResponse
app = FastAPI(description="cookie 使用")
@app.post("/set_cookie")
def set_cookie_demo(user_id: int = Body(description="用户ID")):
resp = JSONResponse(content={"user_id": user_id, "demo": "set_cookie"})
max_age = int(timedelta(hours=6).total_seconds()) # cookie 有效期
resp.set_cookie(key="user_id", value=str(user_id), max_age=max_age)
return resp
@app.get("/get_cookie")
def get_cookie_demo(user_id: int = Cookie(default=0)):
print("user_id", user_id)
resp = JSONResponse(content={"user_id": user_id, "demo": "get_cookie"})
return resp
设置 cookie 可以使用 Response 的 set_cookie 方法进行设置,max_age、expires 参数都是用于指定cookie的有效期,max_age 是指多久到期,expires则是具体的过期时间,使用 expires 时需要注意要使用utc时间。
浏览器存储cookie信息如下
访问其他接口时浏览器会在请求头 Cookie 中带上这个网站设置的cookie信息,服务器获取cookie则直接使用fastapi的Cookie对象就可以直接帮助我们自动解析cookie信息。
注册登陆小Demo
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 注册登陆demo }
# @Date: 2023/08/07 16:08
from datetime import datetime, timedelta
import uvicorn
from fastapi import FastAPI, Body, Cookie
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
app = FastAPI(description="注册登陆demo")
user_infos = [
{"id": 1, "username": "hui", "password": "123456"},
{"id": 2, "username": "quan", "password": "123456"},
]
# key user_id value user info and expire_time
user_session = {
# 1: {"user": {"id": 1, "username": "hui"}, "expire_time": datetime.now() + timedelta(hours=2)}
}
@app.get(path="/index")
def index():
return "index"
@app.get(path="/users/detail")
def user_detail(user_id: int = Cookie(default=0)):
print("user_id", user_id)
# 登陆认证
user_info = user_session.get(user_id)
if not user_info:
return JSONResponse(
content={"code": -1, "message": "未登陆", "data": {}}
)
user = user_info.get("user")
expire_time = user_info.get("expire_time")
print("user", user)
if datetime.now() > expire_time:
return JSONResponse(
status_code=401,
content={"code": -1, "message": "登陆过期,请重新登陆", "data": {}}
)
return JSONResponse(content=user)
def get_user(username, password):
print("user_infos", user_infos)
for user in user_infos:
if user.get("username") == username and user.get("password") == password:
return {"id": user.get("id"), "username": user.get("username")}
class LoginModel(BaseModel):
username: str = Field(min_length=3, max_length=20, description="用户名")
password: str = Field(min_length=6, description="密码")
@app.post(path="/users/register")
def register(user: LoginModel):
print("user", user)
# 获取最大用户ID
max_id = max([user.get("id") for user in user_infos]) or 0
user_id = max_id + 1
# 注册用户,添加到用户列表
register_user = {"id": user_id, **user.model_dump()}
user_infos.append(register_user)
print("user_infos", user_infos)
return JSONResponse(content={"user_id": user_id, "message": "ok"})
@app.post(path="/users/login")
def login(
user: LoginModel
):
user = get_user(user.username, user.password)
print("user", user)
if not user:
return JSONResponse(content={"code": -1, "message": "用户or密码错误", "data": {}})
# 登陆成功设置cookie
resp = JSONResponse(content={"code": 0, "message": "ok", "data": {}})
resp.set_cookie(key="user_id", value=user.get("id"), max_age=int(timedelta(days=1).total_seconds()))
# 保存用户session
user_session[user.get("id")] = {"user": user, "expire_time": datetime.now() + timedelta(days=1)}
return resp
def main():
uvicorn.run(app)
if __name__ == '__main__':
main()
这里并没有使用数据库来存储用户信息以及用户状态信息,而是用 user_infos、user_session 两个全局变量来进行演示这样更简单一些。主要逻辑如下
-
用户注册保存到用户列表
-
用户登陆根据用户列表校验登陆用户信息
- 校验成功设置 cookie 信息
- 保存用户 session 信息(记住用户登陆状态,设置登陆有效期)
-
访问用户详情,根据 cookie 信息校验用户是否登陆
注意:这里主要是展示cookie的使用,真正在业务系统中的一些cookie的设置、密码的注册与校验都会进行加密处理,以保证安全性。
源代码
Github:FastAPI 实战手册
从FastAPI的安装、请求处理、响应返回等基础知识入手,到中间件、数据库操作、身份认证等核心组件的应用,再到实战项目的开发,以及源码分析,循序渐进地介绍FastAPI的使用,旨在让读者全面掌握这个框架。