Skip to content

Django - Cookies 处理

HTTP 是一种无状态协议,这意味着每个请求都是独立的。Cookie 是网站存储在用户浏览器上的一小段数据。它们允许 Web 应用程序在多个请求中记住关于用户的信息,从而本质上增加了状态。

Cookie 的常见用途包括:

  • 会话管理(跟踪已登录用户)。Django 的 session 框架通常会自动处理此问题。
  • 个性化设置(记住用户偏好,如主题或语言)。
  • 跟踪(分析用户行为,尽管这方面存在重要的隐私问题)。

重要注意事项:

  • 客户端存储: Cookie 存储在用户计算机上,这可能对敏感数据不安全。
  • 用户控制: 用户可以配置其浏览器以阻止 Cookie 或删除 Cookie。
  • 大小限制: 浏览器对每个域名的 Cookie 数量和大小施加限制。
  • 安全性: 适当使用 HttpOnly 和 Secure 标志以及 SameSite 属性来缓解跨站脚本(XSS)和跨站请求伪造(CSRF)等风险。
  • 隐私法规: 注意 GDPR 和 CCPA 等关于 Cookie 使用和同意的法规。

Cookie 在视图返回 之前 设置在 HttpResponse 对象上。你使用 response.set_cookie() 方法。

让我们修改一个假设的登录视图,以便设置一个 Cookie 来记住用户的偏好主题。

from django.shortcuts import render, redirect
from django.http import HttpResponse
from .forms import LoginForm # Assuming you have a LoginForm defined
# 假设你定义了一个 LoginForm
import datetime
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
# Simulate successful login
# 模拟成功登录
# ... (authentication logic would go here)
# ... (认证逻辑在这里)
# Create a response object (e.g., redirect to dashboard)
# 创建一个响应对象(例如,重定向到 dashboard)
response = redirect('dashboard') # Assuming 'dashboard' is a named URL
# 假设 'dashboard' 是一个命名的 URL
# Set a cookie for theme preference
# 设置一个用于主题偏好的 cookie
theme = request.POST.get('theme', 'light') # Get theme from form or default
# 从表单获取主题或使用默认值
response.set_cookie(
key='user_theme',
value=theme,
max_age=60*60*24*30, # Expires in 30 days (in seconds)
# 30 天后过期(以秒为单位)
httponly=True, # Prevent JavaScript access (good for security)
# 防止 JavaScript 访问(有利于安全)
samesite='Lax' # Mitigate CSRF risk
# 缓解 CSRF 风险
# secure=True # Uncomment if site uses HTTPS only
# 如果网站只使用 HTTPS,请取消注释
)
# Set another cookie to remember login time
# 设置另一个 cookie 来记住登录时间
response.set_cookie(
'last_login',
str(datetime.datetime.now()),
max_age=60*10 # Expires in 10 minutes
# 10 分钟后过期
)
return response
else:
# Form is invalid, re-render login page with errors
# 表单无效,重新渲染登录页面并显示错误
return render(request, 'login.html', {'form': form})
else:
# GET request, show the login form
# GET 请求,显示登录表单
form = LoginForm()
return render(request, 'login.html', {'form': form})
# A simple dashboard view (target of redirect)
# 一个简单的 dashboard 视图(重定向目标)
def dashboard_view(request):
return HttpResponse("Welcome to your dashboard!")

set_cookie 的关键参数:

  • key:Cookie 的名称(字符串)。
  • value:要存储的值(字符串)。非字符串值会自动转换。
  • max_age:Cookie 的生命周期,以秒为单位。如果为 None 或省略,则它是会话 Cookie(浏览器关闭时删除)。
  • expires:max_age 的替代方案。接受 datetime 对象或格式化字符串。如果两者都设置,max_age 优先。
  • path:Cookie 有效的 URL 路径(默认为 ’/’)。
  • domain:Cookie 有效的域名。
  • secure:如果为 True,则 Cookie 仅通过 HTTPS 发送。
  • httponly:如果为 True,则客户端 JavaScript 无法访问 Cookie(有助于防止 XSS 攻击)。
  • samesite:控制跨站请求行为(‘Strict’、‘Lax’、‘None’)。有助于防止 CSRF 攻击。如果设置为 ‘None’,则需要 secure=True。

浏览器发送的 Cookie 可以在 request.COOKIES 这个类似字典的对象中获取。

让我们创建一个视图,读取之前设置的 user_theme Cookie。

from django.shortcuts import render
def display_preferences_view(request):
# Use .get() for safe access - returns None if cookie doesn't exist
# 使用 .get() 安全访问 - 如果 cookie 不存在则返回 None
user_theme = request.COOKIES.get('user_theme', 'light') # Default to 'light'
# 默认主题为 'light'
last_login_str = request.COOKIES.get('last_login')
context = {
'current_theme': user_theme,
'last_login_time': last_login_str
}
return render(request, 'preferences.html', context)

直接访问 request.COOKIES['cookie_name'] 如果 Cookie 不存在会引发 KeyError。使用 request.COOKIES.get('cookie_name', default_value) 更安全。

要删除 Cookie,使用 response.delete_cookie() 方法。它的工作原理是将 Cookie 的过期日期设置为过去,并将 max_age 设置为 0。

from django.shortcuts import redirect
from django.http import HttpResponse
def logout_view(request):
# Perform logout actions (e.g., clear session data)
# 执行注销操作(例如,清除会话数据)
# ...
response = redirect('home') # Redirect to home page
# 重定向到主页
# Delete the cookies we set earlier
# 删除我们之前设置的 cookie
response.delete_cookie('user_theme')
response.delete_cookie('last_login')
return response
def home_view(request):
return HttpResponse("Welcome home!")
return HttpResponse("欢迎回家!")

如果设置 Cookie 时使用了非默认的 path 和 domain 参数,则删除时必须传递相同的参数给 delete_cookie。

让我们结合设置和读取 Cookie,实现一个基于 last_login Cookie 的简单超时逻辑(尽管 Django sessions 通常是更可靠、更安全的真实身份验证方式)。

import datetime
from django.shortcuts import render, redirect
from django.http import HttpResponse
TIMEOUT_SECONDS = 10 # Short timeout for demonstration
# 短超时用于演示
def protected_page_view(request):
last_login_str = request.COOKIES.get('last_login')
if last_login_str:
try:
# Attempt to parse the stored datetime string
# 尝试解析存储的日期时间字符串
# Note: Storing timezone-aware datetimes is more robust
# 注意:存储带时区的日期时间更健壮
last_login_time = datetime.datetime.strptime(last_login_str.split('.')[0], "%Y-%m-%d %H:%M:%S")
time_difference = datetime.datetime.now() - last_login_time
if time_difference.total_seconds() < TIMEOUT_SECONDS:
# Still within the allowed time
# 仍在允许的时间范围内
return render(request, 'protected_page.html', {'message': 'Welcome back!'})
return render(request, 'protected_page.html', {'message': '欢迎回来!'})
else:
# Timeout expired
# 超时已过期
response = redirect('login') # Redirect to login
# 重定向到登录页
response.delete_cookie('last_login') # Clear the expired cookie
# 清除过期的 cookie
return response
except (ValueError, TypeError):
# Handle cases where the cookie value is invalid
# 处理 cookie 值无效的情况
response = redirect('login')
response.delete_cookie('last_login')
return response
else:
# No login cookie found
# 未找到登录 cookie
return redirect('login')
def protected_page_html(request):
message = request.GET.get('message', 'This is a protected page.')
message = request.GET.get('message', '这是一个受保护的页面。')
return HttpResponse(f"<html><body><h1>Protected Content</h1><p>{message}</p></body></html>")
return HttpResponse(f"<html><body><h1>受保护的内容</h1><p>{message}</p></body></html>")

请记住,你需要为 login、dashboard、logout、home、protected_page 等添加 URL 模式,并创建 protected_page.html 模板,以便此示例能正常工作。

尽管直接操作 Cookie 有用,但它需要谨慎处理安全和状态。对于管理用户登录状态,Django 内置的 session 框架通常是首选且更安全的方法。

延伸阅读: