🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

1. 认识 Cookie

我们知道,cookie 常被用于“用户识别与会话管理”,“用户偏好”存储等,下图是浏览器中 Cookie 的截图,可以协助我们直观的了解 Cookie

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

浏览器中的 Cookie

说到 cookie,自然少不了提到 sessioncookie session 常常协同工作,既提供了用户识别和会话管理的基础,又在安全性和功能性上相互补充,使得 Web 应用程序能够高效、安全地为用户提供个性化服务:

  • cookie 用于在客户端保存用户信息,虽然保存在客户端,但内容却是由服务器来签发的。cookie 保存到客户端之后,后来再向服务端发起请求时都会携带这些 cookie
  • session 用于在服务端保存用户信息。

本文将学习 FastAPI 框架下 cookie 的用法。

2. cookie 的签发

cookie 一般是在用户登录成功后,由服务端签发,并返回给客户端;客户端保存,并在后来每次请求时自动带上 cookie

from typing import Optional
from fastapi import FastAPI, Cookie, Response, Request, Body, Header
from datetime import datetime
import uuid

app = FastAPI()

# 登录成功,服务端签发 token,失败返回错误信息
@app.post("/eg/login")
def login(response: Response, username: str = Body(...), password: str = Body(...)):
    # 服务器端签发 cookie 信息,安全起见要对敏感数据加密
    if username == "admin" and password == "123456":
        # 生成 session id
        session_id = generate_session_id()
        response.set_cookie("session_id", session_id)
        # 设置了 30s 失效
        response.set_cookie('cookie_key', 'cookie_value', max_age=30)
        return {
            "msg": "login success",
            "issue_time": datetime.now().strftime("%d/%m/%Y, %H:%M:%S")
        }
    else:
        return {
            "msg": 'login failed'
        }

上面的 login 示例中,用户登录成功后,服务端就签发了两个 cookie 值,session_id cookie_key。其中 cookie_key 只给了 30s 的有效期。签发的 cookies 通过 Response 带回给请求端。

generate_session_id() 函数,用于模拟生成 session id,代码如下:

# 模拟生成 session id
def generate_session_id():
   # 使用DNS命名空间
    namespace = uuid.NAMESPACE_DNS
    name = 'example.com'
    print(f"uuid1 -> {uuid.uuid1()}")
    print(f"uuid3 -> {uuid.uuid3(namespace, name)}")
    print(f"uuid4 -> {uuid.uuid4()}")
    print(f"uuid5 -> {uuid.uuid5(namespace, name)}")
    return str(uuid.uuid1())

顺带提一下 python 内置的 uuid 库,并没有 uuid2。由于 uuid2 理论上是基于分布式安全服务 DCE(Distributed Computing Environment)的 UUID,这个版本的 UUID 生成依赖于特定的安全服务和协议,而 Python 的标准库并没有直接具有这样的环境,所以并未直接提供它的实现。

运行效果如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

login api

调用 login 登录成功后,可以在浏览器中看到 cookies,如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

浏览器中的 cookies

3. cookie 的获取

和前文介绍的 BodyQueryPath 参数类似,FastAPI 也提供了 Cookie 类及其封装函数来从 HTTP 请求中读取 cookie,类关系图如下。

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

Cookie 类

一般,在用户登录成功后,就会调用其它 Restful API 来获取信息,列如产品列表等,下面示例模拟用户查询产品的接口,代码如下:

@app.get("/eg/query")
def query_products(token: str = Header(...), session_id: Optional[str] = Cookie(None),
                   cookie_key: Optional[str] = Cookie(None)):
    if token == 'token':
        return {
            "msg": 'query success',
            "current_time": datetime.now().strftime("%d/%m/%Y, %H:%M:%S"),
            "cookies": [
                {"session_id": session_id},
                {"cookie_key": cookie_key}
            ]
        }
    else:
        return {
            "msg": 'query failed'
        }

token 一般由登录接口返回,调用上面查询接口时,再把登录接口返回的 token,接口验证 token 合法后,查询并返回数据。这里仅返回 msg、时间和读取到的 cookie,运行效果如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

/eg/query 效果

调用效果如下:由于 cookie_key 签发时,设置了 max_age = 30s,调用接口时已经超过 30s,所以就无法得到有效值了。

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

使用 Cookie 类读取 cookie

4. Cookie 的失效时间

上面提到 cookie 失效导致无法获取有效值,那么 Cookie 失效的时间是怎么决定的呢?这取决于它被设置的方式,主要有以下几种情况:

4.1. 会话 Cookie(Session Cookie)

如果 Cookie 没有明确设置过期时间(Expires 属性 or Max-Age 属性),那么它是一个会话 Cookie。这种类型的 Cookie 只在浏览器的当前会话期间存在,一旦浏览器被关闭,所有会话 Cookie 都会被自动清除。

上面例子中的 session_id 就是一个会话 cookie,你可以关闭浏览器,然后重新打开该地址,可以发现浏览器中已经没有 session_id 了。

4.2. 持久 Cookie(Persistent Cookie)

Cookie 设置了过期时间或最大年龄(Expires or Max-Age),它就成为了一个持久 Cookie。这意味着即使关闭浏览器,Cookie 也会保留在用户的设备上,直到达到所设置的过期时间或最大年龄时间点。例如,如果设置了一个 Cookie 的有效期为 10 天,那么除非用户主动清除 Cookie,否则这个 Cookie 将在 10 天后才失效。

上面 cookie_key 就是设置了 max-age 这种情况,我们来调用 login 接口重新签发一下 cookie

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

login 重新签发 cookies

然后马上调用 query 接口,这时还没有过期,可以得到值,效果如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

没有超过 max-age 可以取到值

30s 之后再调用 query 接口,就无法读取到值了:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

超过 max-age 无法取到值

4.3. 手动清除

用户也可以通过浏览器的隐私设置手动清除 Cookie,这会导致无论 Cookie 的类型或过期时间如何,都会立即失效。

4.4. 浏览器行为

不同的浏览器可能会有不同的行为,特别是在处理会话 Cookie 时。有些浏览器可能在退出而非关闭时清除会话 Cookie,或者在隐私模式下改变 Cookie 的处理方式。

5. 含特殊字符的 cookie

5.1. 签发键名含特殊字符的 cookie

含特殊字符,签发方式没有任何变化,这里不过是 cookie 的键名带有特殊字符 “”,而 python 的变量名是不允许使用连字符 “” 的,这就没法直接在路由函数上使用 Cookie 类来读取 cookie 了。

# 签发名称含特殊字符的 cookie
@app.get("/eg/specialCookie")
def set_cookie_special(response: Response):
    # 设置了 30s 失效
    response.set_cookie('cookieKey', 'cookieValue', max_age=120)
    # 使用了连字符 “-”
    response.set_cookie("cookie-key", "cookie-value")
    return {
        "issue_time": datetime.now().strftime("%d/%m/%Y, %H:%M:%S"),
        "cookies": [
            {"cookieKey": "cookieValue"},
            {"cookie-key": "cookie-value"}
        ]
    }

签发运行效果如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

签发键名带连字符的 cookie

5.2. 读取键名含特殊字符的 cookie

Cookie() 没有提供 Header() 那样的 convert_underscores 来转换下划线, 所以当 cookie key 使用了连字符就不能通过 Cookie() 来获取了,这时可以通过 starlette.requests 模块中的 Request 对象来获取。

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

starlette.requests 模块中的 Request

代码如下:

@app.get("/echo/getSpecialCookie")
async def echo_special_cookie(request: Request):
    return {
        'current_time': datetime.now().strftime("%d/%m/%Y, %H:%M:%S"),
        'cookie-key': request.cookies.get('cookie-key'),
        'cookieKey': request.cookies.get('cookieKey')
    }

运行效果如下:

🚀[Python] FastAPI基础:解锁 Cookie 的用法🚀

使用 Request 对象获取 cookie

6. cookie 和 session 常见使用场景

cookie 用于在客户端保存用户信息,常被用于:

  • 用户识别与会话管理:最基础的功能是识别用户,当用户登录网站时,服务器会创建一个唯一标识(例如 sessionID),并通过 Cookie 发送给用户浏览器。这样,即使在多个页面间跳转,服务器也能通过 Cookie 识别出是同一个用户。
  • 存储用户偏好:如界面语言、主题选择等,提升用户体验。
  • 追踪行为: 被用于网站分析,记录用户的浏览习惯、点击行为等,以便优化网站内容和广告投放。
  • 跨页面数据传递: 可以在不同页面请求间传递小量信息,支持网站功能的连续性。

session 用于在服务端保存用户信息,常被用于:

  • 安全存储用户信息: 保存更多敏感或大量数据,如用户权限信息、购物车内容,比 Cookie 更安全,不易被篡改或窃取。
  • 会话状态维护: 用于跟踪用户的整个会话周期,确保用户在不同请求之间保持一致的状态,列如登录状态、临时数据等。
  • 管理并发与状态: 在处理高并发场景时,每个用户的 Session 独立存储,有助于区分并正确响应每个用户的请求,避免数据混淆。
  • 生命周期管理: Session 一般有更灵活的生命周期管理,可以设置超时时间,过期后自动销毁,保护资源和数据安全。

周末快乐呀

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
点康姆的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容