Django7:权限——登录、会话与访问控制
本系列目录
本系列以 Django 5.2 LTS 和个人博客为例。Django1—Django8 是文章序号。
- Django1:部署——从本地启动到服务器运行
- Django2:配置——看懂项目结构与请求流程
- Django3:数据库——模型设计、关系与迁移
- Django4:ORM——增删改查、事务与查询优化
- Django5:页面——视图、模板、搜索与分页
- Django6:使用——表单、文章管理与 Admin
- Django7:权限——登录、会话与访问控制
- Django8:维护——测试、日志、备份与升级
下载全部教程与可运行示例(ZIP)。解压后进入 django-series/example/,按第一篇运行。
“已经登录”与“有权修改这篇文章”是两个不同判断。一个博客可能有很多作者,Bob 能登录并不意味着他能编辑 Alice 的文章。本篇把账号、会话、路由保护和对象权限连接起来。
本例不开放注册,账号由管理员创建。网站前台允许任何有效的登录作者创建文章,但只能修改和删除自己的内容。后台则由可信管理员维护。
1. 先把权限规则写清楚
| 操作 | 游客 | 普通作者 | 可信管理员 |
|---|---|---|---|
| 查看公开文章 | 可以 | 可以 | 可以 |
| 查看公开地址中的草稿 | 不可以 | 不可以 | 不可以 |
| 创建前台文章 | 需先登录 | 可以 | 可以 |
| 前台编辑、删除自己的文章 | 不可以 | 可以 | 可以 |
| 前台编辑、删除他人的文章 | 不可以 | 不可以 | 不可以 |
| 通过 Admin 管理内容 | 不可以 | 默认不可以 | 按后台权限管理 |
这张表体现本例的明确边界:超级用户在前台也遵守作者筛选,跨作者维护通过 Admin 完成。草稿没有公开预览入口,作者从自己的编辑页面读取草稿。
2. 登录流程是怎样建立身份的
用户提交用户名与密码
→ LoginView 使用认证后端检查凭据
→ 登录成功后建立会话
→ 浏览器保存会话 Cookie
→ 后续请求携带 Cookie
→ SessionMiddleware 与 AuthenticationMiddleware 识别 request.user
默认数据库会话方案中,浏览器持有会话标识,服务器保存对应会话数据。用户密码不会被原样放进 Cookie。账号密码通过 Django 的密码哈希机制管理,不应自己创建一个明文密码字段。Django 认证系统
3. 配置登录与退出入口
config/urls.py 中接入 Django 内置视图:
from django.contrib.auth import views as auth_views
from django.urls import path
# 以下条目加入原有 urlpatterns,保留 admin 和 blog 的路由。
urlpatterns = [
path("accounts/login/", auth_views.LoginView.as_view(), name="login"),
path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout"),
]
settings.py 中设置:
LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "blog:mine"
LOGOUT_REDIRECT_URL = "blog:list"
未登录访问受保护页面时,Django 跳转到登录页,并使用 next 记录原目标。登录成功后可以返回原页面;没有合法目标时使用默认跳转地址。内置 LoginView 会校验跳转目标,不要在自定义视图里把任意外部 next 值直接传给 redirect()。
4. 登录模板和 POST 退出
保存 templates/registration/login.html:
{% extends 'base.html' %}
{% block content %}
<h1>登录</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="hidden" name="next" value="{{ next }}">
<button type="submit">登录</button>
</form>
{% endblock %}
模板使用内置认证表单,登录失败时会展示错误。next 隐藏字段保存登录后目标,CSRF token 保护登录提交。
退出按钮在 base.html 中使用 POST 表单:
<form method="post" action="{% url 'logout' %}">
{% csrf_token %}
<button type="submit">退出</button>
</form>
本系列 Django 5.2 的 LogoutView 不支持用 GET 完成退出。把退出写成普通超链接,可能得到 405。测试中也明确检查了这个行为。
5. 创建普通作者账号
首先用 createsuperuser 创建管理员,再进入 /admin/ 的用户管理添加 Alice 和 Bob。普通作者只需有效账号,不勾选“工作人员状态”,不授予超级用户权限。
也可以在 shell 中交互式创建一个普通用户:
from getpass import getpass
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
User = get_user_model()
user = User(username="alice")
password = getpass("设置 Alice 的密码:")
validate_password(password, user=user)
user.set_password(password)
user.save()
使用 set_password() 或 create_user() 处理哈希。需要注意,底层 create_user() 本身不会自动运行所有密码强度验证器;上例显式调用 validate_password()。后台用户创建表单会结合相应验证规则。密码管理说明
6. 登录保护与对象保护要同时存在
文章编辑视图的核心是:
@login_required
@require_http_methods(["GET", "POST"])
def post_update(request, pk):
post = get_object_or_404(Post, pk=pk, author=request.user)
# 接着绑定并处理 PostForm,完整实现见第六篇。
login_required 回答“是否登录”。查询中的 author=request.user 回答“是否拥有这篇文章”。前端隐藏按钮只能改善交互,不能代替第二个判断。
这里选择返回 404,让无权用户无法通过这个入口区分“对象不存在”和“对象属于别人”。也可以在其他产品中使用 403,但应根据业务一致地设计,并确保 GET 和 POST 都遵守同一规则。
新建时通过 post.author = request.user 设置作者;表单的 fields 不允许作者字段进入用户输入范围。即使 Bob 手工提交 author=Alice的ID,服务器仍把作者设为 Bob。
7. User、Group、模型权限与对象权限
| 概念 | 典型用途 | 本例如何使用 |
|---|---|---|
| User | 表示账号与身份 | 文章作者关联用户 |
| Group | 为一组用户配置权限 | 可用于后台编辑组 |
is_staff |
允许有效用户进入 Admin 入口 | 普通作者不设置 |
is_superuser |
在默认权限机制中具有广泛权限 | 只给可信管理员 |
blog.change_post 等模型权限 |
控制某类模型的操作能力 | Admin 使用此类权限 |
| 对象权限 | 控制某一条具体记录 | 前台按作者过滤实现 |
is_staff 不等同于自动拥有所有模型权限。login_required 也不会自动检查 blog.add_post。本例“所有有效登录作者都能创建前台文章”是主动选择的业务规则。
如果希望只有编辑组才能创建,可以在视图中加 permission_required("blog.add_post", raise_exception=True),并给相应用户组分配权限。即便加入模型权限,编辑和删除仍然需要对象归属判断。Django 默认 ModelBackend 不提供完整的通用对象级权限存储实现;不要假定调用模型权限接口就自动知道文章所有者。自定义认证与权限
8. 要不要自定义用户模型
本教程使用默认用户模型,降低初次搭建复杂度。真实新项目若确定要邮箱登录、组织字段等,最好在首次迁移前定义基于 AbstractUser 的用户模型并设置 AUTH_USER_MODEL。
项目已经建表并存在用户后,再切换用户模型涉及外键和历史迁移,不能把 AUTH_USER_MODEL 改一下就当作完成。无论使用哪种方案,业务模型引用用户优先使用 settings.AUTH_USER_MODEL,运行时获取模型使用 get_user_model()。
9. Cookie、CSRF 与实际登录防护
上线启用 HTTPS 后,本例同时打开安全会话 Cookie 和安全 CSRF Cookie。若只打开安全 Cookie 却继续通过 HTTP 测试登录,浏览器不会按正常 HTTPS 场景发送它们,可能表现为反复登录或 CSRF 失败。
CSRF 防止其他来源借用用户浏览器已有身份提交不受信任的请求;它不解决密码猜测、账号共享或对象权限。登录入口如对公网开放,还应按规模配置适当的限流、失败监控和账号恢复流程。Django 自带登录视图没有自动提供完整的登录失败限速方案。Django 安全说明
本篇验收时应使用不同浏览器会话分别模拟游客、Alice 和 Bob,并直接尝试构造他人的编辑/删除地址。下一篇 Django8:测试与维护 会把这些规则写成可重复执行的测试。
上一篇:Django6:使用——表单、文章管理与 Admin · 下一篇:Django8:维护——测试、日志、备份与升级