本系列目录

本系列以 Django 5.2 LTS 和个人博客为例。Django1—Django8 是文章序号。

  1. Django1:部署——从本地启动到服务器运行
  2. Django2:配置——看懂项目结构与请求流程
  3. Django3:数据库——模型设计、关系与迁移
  4. Django4:ORM——增删改查、事务与查询优化
  5. Django5:页面——视图、模板、搜索与分页
  6. Django6:使用——表单、文章管理与 Admin
  7. Django7:权限——登录、会话与访问控制
  8. 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:维护——测试、日志、备份与升级