bigywater.tech
Django · 78

login_required 与权限装饰器

为什么已经登录的用户点一个没权限的页面,会被送回登录页?装饰器和 Mixin 的行为为什么不一样?这篇把每种情形的响应码和 Location 都列出来。

302 vs 403
已登录但无权限:permission_required 装饰器默认 302 到登录页;raise_exception=True 或 PermissionRequiredMixin 是 403
next
重定向带原路径与查询串,登录页据此跳回
开放重定向
自己处理 next 时要用 url_has_allowed_host_and_scheme 过滤

匿名用户

实测 · 本机执行输出
GET /secret/                   → 302  Location: /accounts/login/?next=/secret/
GET /cbv/                      → 302  Location: /accounts/login/?next=/cbv/
GET /perm-redirect/            → 302  Location: /accounts/login/?next=/perm-redirect/
GET /perm-403/                 → 403
GET /staff/                    → 302  Location: /nostaff/?next=/staff/
GET /secret/?x=1&y=2           → 302  Location: /accounts/login/?next=/secret/%3Fx%3D1%26y%3D2

LOGIN_URL 决定去哪里登录(本实验设为 /accounts/login/)。

已登录但权限不足

实测 · 本机执行输出
GET /secret/                   → 200
GET /perm-redirect/            → 302  Location: /accounts/login/?next=/perm-redirect/
GET /perm-403/                 → 403
GET /pcbv/                     → 403
GET /staff/                    → 302  Location: /nostaff/?next=/staff/

装饰器版本默认重定向,是因为它把「没权限」当成「换个账号试试」。对已经登录的用户这几乎总是错的——用 raise_exception=True,或自己抛 PermissionDenied。

有权限时

实测 · 本机执行输出
GET /secret/                   → 200
GET /perm-redirect/            → 200
GET /perm-403/                 → 200
GET /pcbv/                     → 200
GET /staff/                    → 200
follow=True 之后落在: [('/accounts/login/?next=/secret/%3Fx%3D1%26y%3D2', 302)] login page next=/secret/?x=1&y=2

一切正常。

next 参数的安全

实测 · 本机执行输出
/dashboard/                → 允许跳转: True
//evil.com/                → 允许跳转: False
https://evil.com/x         → 允许跳转: False
https://testserver/x       → 允许跳转: True
javascript:alert(1)        → 允许跳转: False

开放重定向是钓鱼的常用手段:把 next 指到外部站点。

分步演示:访问控制装饰器

login_required 与权限装饰器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。django.test.Client;LOGIN_URL = /accounts/login/
    • 没有验证:AJAX / API 请求下的响应(重定向对它们通常不合适)、REDIRECT_FIELD_NAME 自定义、Django 自带 LoginView 对 next 的过滤实测、多个权限的 permission_required 元组、在 ASGI 下的表现
    京ICP备2026067502号-1京公网安备11010502063487号