一、cookie、session 与登录状态
http 协议本身是无状态的:一次请求结束后,服务器不会自动知道下一次请求是否来自同一个用户。登录功能需要额外的机制保存身份信息,cookie 和 session 就是 django 中最常见的一组方案。
cookie 是浏览器保存的小段键值数据。服务器通过响应头 set-cookie 写入,浏览器在后续请求的 cookie 请求头中自动带回。它适合保存非敏感的偏好设置或一个随机的会话标识,不应该直接保存明文密码。
session 的数据主要保存在服务器端,浏览器通常只保存 sessionid。请求到达 django 后,session 中间件根据这个标识找到服务器端的数据,再通过 request.session 提供给视图使用。默认情况下,session 数据存储在数据库的 django_session 表中。
token 是另一种登录状态方案,常用于前后端分离和 api。服务器签发一个经过签名或加密的令牌,客户端之后在请求头中携带。token 并不等同于 session:它通常由应用或认证库管理,具体格式和存储方式取决于实现。
可以这样理解三者的区别:cookie 是浏览器端的存储载体,session 是服务器端的会话数据,token 是一种可独立传递的身份凭证。生产环境应使用 https,并合理设置 httponly、secure、samesite 等属性。
二、django 操作 cookie
1. 设置 cookie
视图返回 httpresponse、render 返回的响应对象或重定向对象后,都可以调用 set_cookie()。cookie 的值应尽量是简单字符串,不要把密码等敏感信息直接写入浏览器。
from django.http import httpresponse
def login(request):
username = request.post.get("username")
password = request.post.get("password")
if username == "dream" and password == "666":
response = httpresponse("登录成功")
response.set_cookie(
"login_user",
username,
max_age=60 * 60 * 24, # 最长保存 1 天,单位为秒
httponly=true, # 禁止 javascript 直接读取
samesite="lax", # 限制跨站请求携带 cookie
)
return response
return httpresponse("用户名或密码错误", status=401)max_age 表示从当前时刻开始的有效秒数。对于登录状态,更推荐保存随机会话标识,而不是保存用户名、密码或可推导出密码的内容。secure=true 可以要求浏览器仅在 https 连接中发送 cookie,正式环境应启用它。
2. 读取 cookie
请求对象的 cookies 是一个类似字典的对象。cookie 来自客户端,因此只能作为线索使用,不能单独作为权限判断依据。
from django.http import httpresponse
def profile(request):
username = request.cookies.get("login_user")
if not username:
return httpresponse("请先登录", status=401)
return httpresponse(f"当前用户:{username}")3. 删除 cookie 和设置过期时间
删除 cookie 时,django 会返回一个过期的 set-cookie 响应头,浏览器收到后才会移除对应数据。删除时应使用与原 cookie 相同的 path 和 domain 配置。
from django.http import httpresponse
def logout(request):
response = httpresponse("已退出登录")
response.delete_cookie("login_user", path="/")
return responsemax_age=0 可以让 cookie 立即过期;不设置 max_age 或 expires 时,通常是浏览器关闭即失效的会话 cookie。浏览器的具体保留行为还会受到 cookie 属性和隐私策略影响。
三、django 操作 session
使用数据库 session 前,确认 django.contrib.sessions 已加入 installed_apps,并执行迁移:
python manage.py migrate
同时需要启用以下中间件。它负责读取请求中的 session cookie,并在响应阶段保存 session 的变化:
middleware = [
# ...
"django.contrib.sessions.middleware.sessionmiddleware",
# ...
]1. 写入和读取 session
from django.http import httpresponse
def session_login(request):
username = request.post.get("username")
password = request.post.get("password")
if username == "dream" and password == "666":
# django 会在服务器端保存数据,并向浏览器设置 sessionid。
request.session["username"] = username
request.session["is_login"] = true
return httpresponse("登录成功")
return httpresponse("登录失败", status=401)
def dashboard(request):
if not request.session.get("is_login"):
return httpresponse("请先登录", status=401)
username = request.session.get("username")
return httpresponse(f"欢迎,{username}")session 的键值接口与字典类似,但实际数据由 django 的 session 引擎管理。不要把未经保护的密码放入 session;登录场景应使用 django 的认证系统和密码哈希工具。
2. 删除、清空和设置过期时间
def session_logout(request):
# 只删除一个键,其他 session 数据仍然保留。
request.session.pop("is_login", none)
request.session.pop("username", none)
return httpresponse("已退出登录")
def clear_session(request):
# 删除当前会话的所有数据,并使当前 sessionid 失效。
request.session.flush()
return httpresponse("会话已清空")
def set_session_expiry(request):
request.session.set_expiry(60 * 30) # 30 分钟无操作后过期
return httpresponse("会话有效期已设置")request.session.delete() 通常用于删除当前 session 记录;flush() 会同时清除数据并生成新的会话标识,更适合退出登录或防止会话固定攻击的场景。set_expiry(0) 表示浏览器关闭时过期,传入 none 则恢复项目的默认策略。
四、为 cbv 添加 csrf 装饰器
csrf(跨站请求伪造)攻击会诱导已登录用户向目标网站发送非预期请求。django 默认通过 csrfviewmiddleware 保护修改数据的请求。html 表单应包含 {% csrf_token %},api 则可以根据认证方式设计对应的 csrf 策略。
csrf_protect 用于显式启用 csrf 校验,csrf_exempt 用于豁免校验。豁免会降低安全性,只应在明确了解风险并有其他防护措施时使用。
函数视图可以直接使用装饰器;cbv 的方法不是普通函数,需要借助 method_decorator。将装饰器加在 dispatch 上,表示该类的所有请求方法都受到影响;指定 name="post" 则只作用于 post 方法。
from django.utils.decorators import method_decorator
from django.views import view
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.http import httpresponse
@csrf_protect
def function_view(request):
return httpresponse("函数视图已启用 csrf 校验")
@method_decorator(csrf_protect, name="post")
class loginview(view):
def get(self, request, *args, **kwargs):
return httpresponse("登录页面")
def post(self, request, *args, **kwargs):
return httpresponse("登录请求已通过 csrf 校验")
@method_decorator(csrf_exempt, name="dispatch")
class webhookview(view):
def post(self, request, *args, **kwargs):
# 只有在 webhook 使用签名校验等替代方案时才考虑豁免。
return httpresponse("webhook 已接收")直接在 cbv 的 post() 方法上写普通的 @csrf_exempt,通常不会按预期工作,因为 django 最终调用的是经过 as_view() 暴露的类视图入口。使用 method_decorator 可以明确地把函数装饰器适配到类视图。
五、中间件与请求生命周期
中间件是位于请求和视图之间的一层可插拔处理逻辑。请求进入时,中间件可以检查或修改 request;视图返回响应后,中间件还可以修改 response。多个中间件按照 middleware 的顺序进入,响应阶段通常按相反顺序返回。
常见内置中间件的职责:
securitymiddleware:提供 https 重定向、hsts 等安全相关处理。sessionmiddleware:加载和保存request.session。commonmiddleware:处理规范化 url、请求头等通用逻辑。csrfviewmiddleware:校验 csrf token。authenticationmiddleware:在 session 基础上提供request.user。messagemiddleware:支持 django 消息框架。xframeoptionsmiddleware:限制页面被嵌入 iframe,降低点击劫持风险。
顺序很重要:例如 authenticationmiddleware 依赖 sessionmiddleware,因此必须放在它后面。
1. 自定义中间件
现代 django 推荐编写一个接收 get_response 的可调用对象。下面的例子记录请求耗时,并把结果写入响应头。
# users/middleware.py
import time
class requesttimingmiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
start = time.perf_counter()
response = self.get_response(request)
elapsed = time.perf_counter() - start
response["x-request-time"] = f"{elapsed:.4f}s"
return responseget_response(request) 会继续调用下一个中间件或最终视图。必须返回一个响应对象,否则 django 无法完成请求。中间件应保持职责单一,避免在其中堆积具体业务逻辑。
2. 注册自定义中间件
# settings.py
middleware = [
"django.middleware.security.securitymiddleware",
"django.contrib.sessions.middleware.sessionmiddleware",
"django.middleware.common.commonmiddleware",
"django.middleware.csrf.csrfviewmiddleware",
"django.contrib.auth.middleware.authenticationmiddleware",
"django.contrib.messages.middleware.messagemiddleware",
"django.middleware.clickjacking.xframeoptionsmiddleware",
"users.middleware.requesttimingmiddleware",
]路径必须写成“应用名.模块名.类名”。修改 middleware 后应重启开发服务器,并确认该类可以被 django 正常导入。
3. 观察执行顺序
如果需要学习或排查中间件顺序,可以临时打印不同阶段的日志:
from django.utils.deprecation import middlewaremixin
class debugmiddleware(middlewaremixin):
def process_request(self, request):
print("1. process_request")
def process_view(self, request, view_func, view_args, view_kwargs):
print("2. process_view")
def process_response(self, request, response):
print("3. process_response")
return responseprocess_request 在视图执行前调用,process_view 紧接在视图调用前,process_response 在响应返回客户端前调用。调试完成后应移除 print,改用日志系统记录必要信息。
六、总结与练习方向
- cookie 存在浏览器端,session 数据主要存在服务器端;二者通常通过一个 session cookie 联系起来。
- 登录状态不应依赖客户端可随意修改的明文信息,权限判断应使用可靠的服务端会话或认证机制。
- csrf 保护默认应保持开启;cbv 使用
method_decorator指定装饰器作用范围。 - 中间件适合做认证、日志、统一响应头和异常处理等横切逻辑,执行顺序由
middleware配置决定。 - 可以在图书管理系统中加入注册、登录、退出功能,并使用 session 保护“新增、修改、删除图书”等页面。
到此这篇关于django 状态管理与请求处理:cookie、session、csrf 和中间件详解的文章就介绍到这了,更多相关django cookie、session、csrf和中间件内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论