Chapter 08

用户认证与权限

Django 自带一套完整的认证系统:用户、登录登出、权限、组。本章教你安全地管理「谁是谁、谁能做什么」。

8.1 认证系统总览

django.contrib.auth 提供开箱即用的用户模型、密码哈希、登录会话、权限与分组。它是 Admin 的地基,也是你自己前台登录的地基。

User 模型
内置用户模型,含 username/password/email/is_staff/is_superuser/is_active 等字段。
authenticate()
校验用户名密码,成功返回 User 对象、失败返回 None。它验证凭据但不建立会话。
login() / logout()
login 在会话中记录用户身份,logout 清除会话。二者操作 session,不校验密码。
@login_required
视图装饰器,未登录用户访问将被重定向到登录页(由 LOGIN_URL 指定)。
Permission(权限)
细粒度授权。每个模型自动获得 add/change/delete/view 四种权限,可授予用户或组。
Group(组)
权限的集合。把权限授予组、把用户加入组,批量管理权限,如「编辑组」「审核组」。
密码哈希(Password Hashing)
Django 永不存明文密码,默认用 PBKDF2 加盐哈希。set_password 加密、check_password 校验。

8.2 登录与登出视图

from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect

def login_view(request):
    if request.method == "POST":
        user = authenticate(
            request,
            username=request.POST["username"],
            password=request.POST["password"],
        )
        if user is not None:
            login(request, user)      # 建立会话
            return redirect("blog:list")
    return render(request, "login.html")

def logout_view(request):
    logout(request)
    return redirect("login")
用内置认证视图更省心

Django 提供 django.contrib.auth.urls,一行 include 即得 login/logout/password_change/password_reset 全套视图,你只需提供模板。生产项目优先复用它们。

8.3 保护视图

from django.contrib.auth.decorators import login_required, permission_required

@login_required
def dashboard(request):
    return render(request, "dashboard.html")

@permission_required("blog.add_post", raise_exception=True)
def new_post(request):
    ...

模板里判断登录状态与权限:

{% if user.is_authenticated %}
  欢迎,{{ user.username }}
  {% if perms.blog.add_post %}<a>写文章</a>{% endif %}
{% else %}
  <a href="{% url 'login' %}">登录</a>
{% endif %}

8.4 权限与组

from django.contrib.auth.models import Group, Permission

# 检查权限
user.has_perm("blog.change_post")     # True / False

# 建组并授权
editors = Group.objects.create(name="编辑")
perm = Permission.objects.get(codename="add_post")
editors.permissions.add(perm)

# 用户加入组,即拥有组的全部权限
user.groups.add(editors)

8.5 自定义 User 模型

官方强烈建议项目一开始就自定义 User(哪怕暂时只是空壳),后期再改极其痛苦。

# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    phone = models.CharField(max_length=20, blank=True)
    avatar = models.ImageField(upload_to="avatars/", blank=True)

# settings.py
AUTH_USER_MODEL = "accounts.User"
AUTH_USER_MODEL 必须在首次 migrate 前设定

一旦数据库已按默认 User 建表,中途切换自定义 User 会引发大量迁移冲突。新项目第一步就配好 AUTH_USER_MODEL,并全程用 get_user_model() 引用用户模型。

8.6 密码安全

from django.contrib.auth import get_user_model
User = get_user_model()

u = User(username="alice")
u.set_password("s3cret")   # 加盐哈希,切勿直接赋值 u.password
u.save()

u.check_password("s3cret") # True

8.7 会话相关配置

配置含义
LOGIN_URL未登录时的跳转地址
LOGIN_REDIRECT_URL登录成功后默认跳转
LOGOUT_REDIRECT_URL登出后跳转
SESSION_COOKIE_AGE会话有效期(秒)
SESSION_COOKIE_SECURE仅 HTTPS 传 cookie

8.8 小结与下一步