第 3 章 · 用户认证、权限与会话
本章目标:掌握 Django 内置用户认证系统;使用 LoginView / LogoutView 实现登录登出;用 @login_required 保护商品管理视图;理解 Group(用户组) 与 Permission 模型;了解对象级权限的概念与常见实现思路;配置 Session 与登录跳转。
学时建议:4~5 小时(含 1.5 小时跟练)
前置:完成 django-web ch01~ch02(shopdemo、Product 模型、ModelForm 创建/编辑流程)。
3.1 场景说明:谁能改商品?
shop-demo 电商后台需要区分角色:
| 角色 | 权限 | Django 实现 |
|---|---|---|
| 超级管理员 | 全部功能 + Admin | is_superuser=True |
| 商品运营 | 增删改商品,不可管用户 | 自定义 Group + Permission |
| 只读访客 | 仅查看列表 | @login_required + 视图内判断 |
| 未登录用户 | 跳转登录页 | LoginRequiredMixin / 装饰器 |
本章在本地虚构环境演练。对外 API 域名 api.example.com 仅作后续 DRF 联调占位,不涉及真实业务系统。
3.2 Django 认证体系概览
django.contrib.auth
├── User # 默认用户模型(用户名、密码哈希、is_staff 等)
├── Group # 用户组,可批量赋权
├── Permission # 权限点,格式 app_label.codename
└── Session # 登录态存储(数据库 / 缓存 / Cookie)
| 模型/配置 | 作用 |
|---|---|
User | 账号主体,username + password |
Group | 如「商品运营」「财务审核」 |
Permission | 如 catalog.change_product |
Session | 服务端保存 sessionid,标识已登录用户 |
认证流程:
POST /accounts/login/ (username + password)
→ authenticate() 验证密码哈希
→ login(request, user) 写入 Session
→ 后续请求携带 sessionid Cookie
→ AuthenticationMiddleware 注入 request.user
3.3 启用认证相关 App
settings.py 确认已包含(startproject 默认即有):
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"catalog",
"accounts",
]
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",
]
登录相关配置:
LOGIN_URL = "/accounts/login/"
LOGIN_REDIRECT_URL = "/catalog/"
LOGOUT_REDIRECT_URL = "/accounts/login/"
| 配置项 | 含义 |
|---|---|
LOGIN_URL | 未登录访问受保护页面时的跳转地址 |
LOGIN_REDIRECT_URL | 登录成功后默认去向 |
LOGOUT_REDIRECT_URL | 登出后跳转 |
3.4 使用 LoginView 与 LogoutView
Django 提供基于类的认证视图,无需手写密码校验逻辑。
accounts/urls.py:
from django.contrib.auth import views as auth_views
from django.urls import path
urlpatterns = [
path(
"login/",
auth_views.LoginView.as_view(
template_name="accounts/login.html",
redirect_authenticated_user=True,
),
name="login",
),
path(
"logout/",
auth_views.LogoutView.as_view(),
name="logout",
),
]
根 shopdemo/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("accounts/", include("accounts.urls")),
path("catalog/", include("catalog.urls")),
]
accounts/templates/accounts/login.html(核心字段):
<form method="post">
{% csrf_token %}
<input type="text" name="username" required>
<input type="password" name="password" required>
<button type="submit">登录</button>
</form>
LoginView默认接收字段名username与password,与User模型一致。
创建测试账号:
python manage.py createsuperuser
# 用户名: admin_demo 密码: 教学用强密码(勿用于任何真实系统)
python manage.py shell
from django.contrib.auth.models import User
User.objects.create_user("operator_demo", password="TeachPass123!")
3.5 @login_required 保护视图
catalog/views.py:
from django.contrib.auth.decorators import login_required, permission_required
@login_required
def product_list(request):
products = Product.objects.filter(is_active=True).select_related("category")
return render(request, "catalog/product_list.html", {"products": products})
@login_required
@permission_required("catalog.add_product", raise_exception=True)
def product_create(request):
# ... 与 ch02 相同
...
@login_required