# Django 5.2.8 documentation

# Django 中的自定义验证

Django 自带的身份验证已经足够满足大多数常见的情况，但你可能有一些需求没有被开箱即用的默认值所满足。在你的项目中定制身份验证需要了解所提供系统的哪些点是可扩展或可替换的。本文档详细介绍了如何定制认证系统。

当用户模型中存储的用户名和密码需要对一个不同于 Django 默认服务验证时，认证后端 提供了一个可扩展的系统。

你可以给你的模型 自定义权限 并且可以通过 Django 的授权系统检查。

你可以 扩展 默认的 `User` 模型，或者完全自定义一个模型进行 替换

## 其它认证资源

有时候你需要连接到其他认证源——一个包含用户名及密码的源或者认证方法。

例如，你的公司可能已经有了一个 LDAP 配置，为每个员工存储了一个用户名和密码。如果用户在 LDAP 和基于 Django 的应用程序中分别有不同的账户，那么对于网络管理员和用户本身来说都是一件很麻烦的事情。

所以，为了处理这样的情况，Django 认证系统让你可以插入其他认证源。你可以覆盖 Django 默认的基于数据库的方案，也可以将默认系统与其他系统串联使用。

参见 认证后端参考，了解 Django 所包含的认证后端的信息。

### 指定认证后端

Behind the scenes, Django maintains a list of "authentication backends" that it checks for authentication. When somebody calls `django.contrib.auth.authenticate()` -- as described in How to log a user in -- Django tries authenticating across all of its authentication backends. If the first authentication method fails, Django tries the second one, and so on, until all backends have been attempted.

要使用的认证后端列表在 `AUTHENTICATION_BACKENDS` 配置中指定。这应该是一个指向知道如何验证的 Python 类的 Python 路径名列表。这些类可以是 Python 路径上的任何地方。

默认情况下，`AUTHENTICATION_BACKENDS` 配置为：

    ["django.contrib.auth.backends.ModelBackend"]

这是基本的身份验证后端，它会检查 Django 用户数据库并查询内置权限。它不提供任何速率限制机制来防止暴力攻击。你可以在自定义身份验证后端中实现自己的速率限制机制，或者使用大多数 Web 服务器提供的机制。

`AUTHENTICATION_BACKENDS` 的顺序很重要，所以如果同一个用户名和密码在多个后端都有效，Django 会在第一个正向匹配时停止处理。

如果一个后端抛出 `PermissionDenied` 异常，则验证流程立马终止，Django 不会继续检查其后的后端。

Note

一旦用户进行了身份验证，Django 会在用户会话中存储用于验证用户的后端，并在会话的整个持续时间内重复使用同一后端，每当需要访问当前经过身份验证的用户时。这实际上意味着身份验证源会按会话基础进行缓存，因此如果您更改了 `AUTHENTICATION_BACKENDS`，您需要清除会话数据，以便强制用户使用不同的方法重新进行身份验证。一个简单的方法是执行 `Session.objects.all().delete()`。

### 编写一个认证后端

认证后端是一个类，它实现了两个必要的方法：`get_user(user_id)` 和 `authenticate(request, **credentials)`，以及一组可选的与权限相关的 认证方法。

`get_user` 方法接收一个 `user_id` ——可以是用户名、数据库 ID 或其他什么，但必须是用户对象的主键——然后返回一个用户对象或 `None`。

`authenticate` 方法采用 `request` 参数和证书作为关键字参数。大多数情况下，它看起来像这样：

    from django.contrib.auth.backends import BaseBackend

    class MyBackend(BaseBackend):
        def authenticate(self, request, username=None, password=None):
            # Check the username/password and return a user.
            ...

但它也可以认证一个令牌，比如：

    from django.contrib.auth.backends import BaseBackend

    class MyBackend(BaseBackend):
        def authenticate(self, request, token=None):
            # Check the token and return a user.
            ...

无论哪种方式，`authenticate()` 应该检查它所得到的凭证，如果凭证有效，则返回一个与这些凭证相匹配的用户对象。如果无效，则应返回 `None`。

`request` 是一个 `HttpRequest`，如果没有提供给 `authenticate()` （将其传递给后端），则可能是 `None`。

Django 管理后台与 Django 的 用户对象 紧密耦合。例如，用户要访问管理后台，`User.is_staff` 和 `User.is_active` 必须为 ``True``（详情请参阅 `AdminSite.has_permission()`）。

处理这个问题的最佳方法是为后端存在的每个用户（例如，在你的 LDAP 目录、外部 SQL 数据库等中）创建一个 Django `User` 对象。你可以提前编写一个脚本来完成此操作，或者你的 `authenticate` 方法可以在用户首次登录时完成此操作。

这是一个示例后端，它根据在 `settings.py` 文件中定义的用户名和密码变量进行身份验证，并在用户首次认证时创建一个 Django `User` 对象。在这个示例中，创建的 Django `User` 对象是一个超级用户，他将拥有对管理后台的完全访问权限：

    from django.conf import settings
    from django.contrib.auth.backends import BaseBackend
    from django.contrib.auth.hashers import check_password
    from django.contrib.auth.models import User

    class SettingsBackend(BaseBackend):
        """
        Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

        Use the login name and a hash of the password. For example:

        ADMIN_LOGIN = 'admin'
        ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
        """

        def authenticate(self, request, username=None, password=None):
            login_valid = settings.ADMIN_LOGIN == username
            pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
            if login_valid and pwd_valid:
                try:
                    user = User.objects.get(username=username)
                except User.DoesNotExist:
                    # Create a new user. There's no need to set a password
                    # because only the password from settings.py is checked.
                    user = User(username=username)  # is_active defaults to True.
                    user.is_staff = True
                    user.is_superuser = True
                    user.save()
                return user
            return None

        def get_user(self, user_id):
            try:
                return User.objects.get(pk=user_id)
            except User.DoesNotExist:
                return None

### 在自定义后端处理认证

自定义的认证后端可以提供他们自己的权限。

The user model and its manager will delegate permission lookup functions (`get_user_permissions()`, `get_group_permissions()`, `get_all_permissions()`, `has_perm()`, `has_module_perms()`, and `with_perm()`) to any authentication backend that implements these functions.

用户所拥有的权限将是所有验证后端返回的所有权限的一个超集。也就是说，如果任何后端之一将一个权限赋予了用户，那么 Django 最终也将该权限赋予这个用户。

If a backend raises a `PermissionDenied` exception in `has_perm()` or `has_module_perms()`, the authorization will immediately fail and Django won't check the backends that follow.

后端可以像这样为管理员实现权限：

    from django.contrib.auth.backends import BaseBackend

    class MagicAdminBackend(BaseBackend):
        def has_perm(self, user_obj, perm, obj=None):
            return user_obj.username == settings.ADMIN_LOGIN

这就给了上例中授予访问权限的用户全部权限。请注意，除了给相关的 `django.contrib.auth.models.User` 函数提供相同的参数外，后台的认证函数都以用户对象（可能是一个匿名用户）作为参数。

一个完整的鉴权实现可以在 django/contrib/auth/backends.py 的 `ModelBackend` 里找到，这是默认的后端，并且在大多数时候会查询 `auth_permission` 表。

#### 匿名用户的授权

匿名用户是指那些没有验证过的用户，也就是说，他们没有提供任何有效的验证信息。然而，这并不一定意味着他们就无权做任何事。在最基本的层面上，大多数站点允许匿名用户浏览大部分页面，而且很多站点也允许匿名评论。

Django 的权限框架没有用于存储匿名用户权限的地方。但是，传递给身份验证后端的用户对象可以是一个 `django.contrib.auth.models.AnonymousUser` 对象，允许后端为匿名用户指定自定义授权行为。这对于可重用应用程序的作者特别有用，他们可以将所有授权问题委托给身份验证后端，而不需要设置来控制匿名访问，例如。

#### 非激活用户认证

非激活用户是指 `is_active` 字段设置为 `False` 的用户。`ModelBackend` 和 `RemoteUserBackend` 认证后端禁止这些用户进行认证。如果一个自定义用户模型没有 `is_active` 字段，那么所有用户都将被允许认证。

如果你想让非激活用户进行身份认证，可以使用 `AllowAllUsersModelBackend` 或 `AllowAllUsersRemoteUserBackend`。

权限系统中对匿名用户的支持，可以实现匿名用户有权限做某事，而非激活的认证用户没有权限的场景。

不要忘记在自己的后端权限方法中测试用户的 `is_active` 属性。

#### 处理对象权限

Django 的权限框架有一个对象权限的基础，尽管在核心中没有实现。这意味着对对象权限的检查总是返回 `False` 或一个空列表（取决于所执行的检查）。认证后端将为每个与对象相关的授权方法接收关键字参数 `obj` 和 `user_obj`，并可以适当返回对象级别的权限。

## 自定义权限

要为给定的模型对象创建自定义权限，使用 `permissions` 模型 Meta 属性。

这个 `Task` 模型创建了两个自定义权限，即用户可以或不可以对 `Task` 实例进行的操作，具体到你的应用程序：

    class Task(models.Model):
        ...

        class Meta:
            permissions = [
                ("change_task_status", "Can change the status of tasks"),
                ("close_task", "Can remove a task by setting its status as closed"),
            ]

这样做的唯一作用就是当你运行 `manage.py migrate` 时，创建这些额外的权限（创建权限的函数连接到 `post_migrate` 信号）。你的代码负责在用户试图访问应用程序提供的功能（改变任务状态或关闭任务）时检查这些权限的值。 继续上面的例子，下面检查用户是否可以关闭任务：

    user.has_perm("app.close_task")

## 扩展现有的 `User` 模型

有两种方法可以扩展默认的 `User` 模型，而不用替换自己的模型。如果你需要的是纯粹的行为改变，而不需要改变数据库中存储的内容，你可以基于 `User` 创建一个 代理模型。这允许代理模型提供的任何功能，包括默认排序、自定义管理器或自定义模型方法。

如果你希望存储与 `User` 相关的信息，你可以使用一个 `OneToOneField` 到一个包含字段的模型，以获得额外的信息。这种一对一的模型通常被称为 profile 模型，因为它可能会存储一个站点用户的非认证相关信息。例如，你可以创建一个 Employee 模型：

    from django.contrib.auth.models import User

    class Employee(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        department = models.CharField(max_length=100)

假设存在一个名为 Fred Smith 的员工，他既有一个 User 模型又有一个 Employee 模型，您可以使用 Django 的标准关联模型约定来访问相关信息：

    >>> u = User.objects.get(username="fsmith")
    >>> freds_department = u.employee.department

要将配置文件模型的字段添加到管理员的用户页面中，在你的应用程序的 `admin.py` 中定义一个 `InlineModelAdmin` （在这个例子中，我们将使用一个 `StackedInline`），并将其添加到一个 `UserAdmin` 类中，该类用 `User`` 类注册：

    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
    from django.contrib.auth.models import User

    from my_user_profile_app.models import Employee

    # Define an inline admin descriptor for Employee model
    # which acts a bit like a singleton
    class EmployeeInline(admin.StackedInline):
        model = Employee
        can_delete = False
        verbose_name_plural = "employee"

    # Define a new User admin
    class UserAdmin(BaseUserAdmin):
        inlines = [EmployeeInline]

    # Re-register UserAdmin
    admin.site.unregister(User)
    admin.site.register(User, UserAdmin)

这些 profile 模型并没有任何特殊之处——它们只是 Django 模型，恰好与用户模型有一对一的联系。因此，当用户被创建时，它们并不会自动创建，但可以用一个 `django.db.models.signals.post_save` 来适当地创建或更新相关模型。

使用相关模型会导致额外的查询或连接来检索相关数据。根据你的需求，包含相关字段的自定义用户模型可能是你更好的选择，然而，在你的项目应用程序中与默认用户模型的现有关系可能会证明额外的数据库负载是合理的。

## 替换一个自定义的 `User` 模型。

有些项目可能会有认证需求，而 Django 内置的 `User` 模型并不总是合适。例如，在一些网站上，使用电子邮件地址作为你的身份识别标记比使用用户名更有意义。

Django 允许你通过为 `AUTH_USER_MODEL` 配置提供一个引用自定义模型的值来覆盖默认的用户模型：

    AUTH_USER_MODEL = "myapp.MyUser"

这个点对描述了 Django 应用程序的 `label` （必须在您的 `INSTALLED_APPS` 中），以及您希望用作用户模型的 Django 模型的名称。

### 启动项目时使用自定义用户模型

如果你正在启动一个新项目，可以通过继承 `AbstractUser` 来设置一个与默认用户模型行为相同的自定义用户模型：

    from django.contrib.auth.models import AbstractUser

    class User(AbstractUser):
        pass

不要忘记将 `AUTH_USER_MODEL` 指向它。在创建任何迁移或者首次运行 `manage.py migrate` 之前执行这个操作。

同样的，在 app 中的 `admin.py` 中注册模型。

    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    from .models import User

    admin.site.register(User, UserAdmin)

### 在项目中更改为自定义用户模型。

在创建数据库表后更改 `AUTH_USER_MODEL` 是可行的，但可能会很复杂，因为它会影响外键和多对多关系等。

这个改动并不能自动完成，需要手动修复你的架构，将数据从旧的用户表移出，并有可能需要手动执行一些迁移操作。查看步骤概述，请查看 #25313。

由于 Django 对可交换模型的动态依赖特性的限制，`AUTH_USER_MODEL` 所引用的模型必须在其应用的第一次迁移中创建（通常称为 `0001_initial`）；否则，你会出现依赖问题。

此外，当你运行迁移时，你可能会遇到一个 `CircularDependencyError`，因为 Django 不会因为动态依赖而自动打破依赖循环。如果你看到这个错误，你应该通过将你的用户模型所依赖的模型移动到第二个迁移中来打破这个循环。（如果你想看看通常是怎么做的，你可以尝试做两个正常的模型，它们之间有一个 `ForeignKey`，看看 `makemigrations` 是如何解决这个循环依赖的。）

### 可重用应用和 `AUTH_USER_MODEL`

可重用应用不应该实现自定义用户模型。一个项目可能会使用很多应用，两个实现了自定义用户模型的可重用应用不能一起使用。如果你需要在你的应用中存储每个用户的信息，可以使用一个 `ForeignKey` 或 `OneToOneField` 到 `settings.AUTH_USER_MODEL`，如下所述。

### 引用 `User` 模型

如果你直接引用 `User` （例如，通过在一个外键中引用它），你的代码将无法在 `AUTH_USER_MODEL` 配置已被更改为不同用户模型的项目中工作。

get_user_model()

  与其直接引用 `User`，不如使用 `django.contrib.auth.get_user_model()` 引用用户模型。这个方法将返回当前活动的用户模型——如果指定了自定义用户模型，则返回自定义用户模型，否则返回 `User`。

  在定义用户模型的外键或多对多关系时，应使用 `AUTH_USER_MODEL` 配置指定自定义模型。例如：

      from django.conf import settings
      from django.db import models

      class Article(models.Model):
          author = models.ForeignKey(
              settings.AUTH_USER_MODEL,
              on_delete=models.CASCADE,
          )

  连接用户模型发送的信号时，应使用 `AUTH_USER_MODEL` 配置指定自定义模型。例如：

      from django.conf import settings
      from django.db.models.signals import post_save

      def post_save_receiver(sender, instance, created, **kwargs):
          pass

      post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)

  一般来说，在导入时执行的代码中，用 `AUTH_USER_MODEL` 配置来引用用户模型是最简单的，不过，也可以在 Django 导入模型时调用 `get_user_model()`，所以可以使用 `models.ForeignKey(get_user_model()，...)`。

  例如，如果你的应用是用多个用户模型进行测试的，使用 `@override_settings(AUTH_USER_MODEL=...)`，并且你把 `get_user_model()` 的结果缓存在模块级变量中，你可能需要监听 `setting_changed` 信号来清除缓存。例如：

      from django.apps import apps
      from django.contrib.auth import get_user_model
      from django.core.signals import setting_changed
      from django.dispatch import receiver

      @receiver(setting_changed)
      def user_model_swapped(*, setting, **kwargs):
          if setting == "AUTH_USER_MODEL":
              apps.clear_cache()
              from myapp import some_module

              some_module.UserModel = get_user_model()

### 指定自定义用户模型

当你用自定义用户模型开始你的项目时，请停下来考虑一下这是否是你项目的正确选择。

将所有用户相关信息保存在一个模型中，就不需要额外的或更复杂的数据库查询来检索相关模型。另一方面，在一个与你的自定义用户模型有关系的模型中存储特定应用的用户信息可能更合适。这允许每个应用程序指定自己的用户数据需求，而不会有潜在的冲突或破坏其他应用程序的假设。这也意味着，你会让你的用户模型尽可能的简单，专注于认证，并遵循 Django 期望自定义用户模型满足的最低要求。

如果你使用默认的认证后端，那么你的模型必须有一个可用于识别目的的唯一字段。这可以是一个用户名，一个电子邮件地址，或任何其他独特的属性。如果你使用可以支持的自定义认证后端，则允许使用非唯一的用户名字段。

最简单的方法是继承 `AbstractBaseUser`。 `AbstractBaseUser` 提供了用户模型的核心实现，包括哈希密码和令牌化密码重置。然后你必须提供一些关键的实现细节。

class models.CustomUser
  USERNAME_FIELD

    一个字符串，描述了在用户模型上用作唯一标识符的字段的名称。通常，这将是某种用户名，但它也可以是电子邮件地址或任何其他唯一标识符。该字段 必须 是唯一的（例如，在其定义中设置了 `unique=True`），除非您使用可以支持非唯一用户名的自定义身份验证后端。

    接下来的样例中，`identifier` 字段将被用作识别字段：

        class MyUser(AbstractBaseUser):
            identifier = models.CharField(max_length=40, unique=True)
            ...
            USERNAME_FIELD = "identifier"

  EMAIL_FIELD

    用来描述用户模型中的邮件字段，该值通过 `get_email_field_name()` 返回。

  REQUIRED_FIELDS

    在通过 `createsuperuser` 管理命令创建用户时，将提示用户输入的字段名称列表。用户将被提示为每个字段提供一个值。它必须包括 `blank` 为 `False` 或未定义的任何字段，并可能包括你希望在交互式创建用户时提示的其他字段。`REQUIRED_FIELDS` 在 Django 的其他部分没有效果，比如在管理中创建用户。

    例如，这里是一个用户模型的部分定义，它定义了两个必填字段——出生日期和身高：

        class MyUser(AbstractBaseUser):
            ...
            date_of_birth = models.DateField()
            height = models.FloatField()
            ...
            REQUIRED_FIELDS = ["date_of_birth", "height"]

    Note

    `REQUIRED_FIELDS` 必须包含用户模型上的所有必填字段，但 不应 包含 `USERNAME_FIELD` 或 `password`，因为这些字段总是会被提示。

  is_active

    A boolean attribute that indicates whether the user is considered "active". This attribute is provided as an attribute on `AbstractBaseUser` defaulting to `True`. How you choose to implement it will depend on the details of your chosen auth backends. See the documentation of the `is_active attribute on the built-in user model` for details.

  get_full_name()

    可选项。用户的较长身份标识符，比如用户的全名。如果已经设置，则会与用户名一起出现在 `django.contrib.admin` 中。

  get_short_name()

    可选项。用户较短的身份标识符，比如用户的名。如果已经设置，它会在 `django.contrib.admin` 页面头部的欢迎词中替换用户名。

  导入 `AbstractBaseUser`

  `AbstractBaseUser` and `BaseUserManager` 可以从 `django.contrib.auth.base_user` 中导入，所以你无需在 `INSTALLED_APPS` 添加 `django.contrib.auth` 就能导入它们。

`AbstractBaseUser` 的任何子类都可以使用下面的属性和方法：

class models.AbstractBaseUser
  get_username()

    返回 `USERNAME_FIELD` 指定的字段的值。

  clean()

    通过调用 `normalize_username()` 来规范化用户名。 如果重写此方法，必须调用 `super()` 来保持规范化。

  classmethod get_email_field_name()

    返回由 `EMAIL_FIELD` 属性指定的电子邮件字段的名称。 如果未指定 `EMAIL_FIELD` ，则默认为 `'email'` 。

  classmethod normalize_username(username)

    应用 NFKC Unicode 规范化用户名，使得不同 Unicode 码位视觉相同字符视为相同。

  is_authenticated

    只读属性，始终返回 `True` （匿名用户 `AnonymousUser.is_authenticated` 始终返回 `False` ）。这是一种判断用户是否已通过身份验证的方法。这并不意味着任何权限，也不会检查用户是否处于活动状态或是否具有有效会话。即使通常您会根据 `request.user` 检查这个属性，以确定它是否被 `AuthenticationMiddleware` 填充（表示当前登录的用户），但是你应该知道该属性对于任何 `User` 实例都返回 `True`。

  is_anonymous

    只读属性总是 `False`。这个属性用于区分 `User` 和 `AnonymousUser` 对象。通常情况下， `is_authenticated` 应该置于只读。

  set_password(raw_password)

    设置用户密码，谨慎保存密码哈希。不可保存 `AbstractBaseUser` 的对象。

    When the raw_password is `None`, the password will be set to an unusable password, as if `set_unusable_password()` were used.

  check_password(raw_password)
  acheck_password(raw_password)

    异步版本：`acheck_password()`

    如果给定的原始字符串是用户的正确密码，返回 `True`。（在进行比较时，这将处理密码散列。）

  set_unusable_password()

    Marks the user as having no password set. This isn't the same as having a blank string for a password. `check_password()` for this user will never return `True`. Doesn't save the `AbstractBaseUser` object.

    如果你的应用程序的身份验证是针对现有的外部源（如 LDAP 目录）进行的，你可能需要这个功能。

  has_usable_password()

    Returns `False` if `set_unusable_password()` has been called for this user.

  get_session_auth_hash()

    返回密码字段的 HMAC。用于 session-invalidation-onpassword-change。

  get_session_auth_fallback_hash()

    使用 `SECRET_KEY_FALLBACKS` 生成密码字段的 HMAC。被 `get_user()` 使用。

`AbstractUser` 是 `AbstractBaseUser` 的子类。

class models.AbstractUser
  clean()

    通过调用 `BaseUserManager.normalize_email()` 来规范化邮件。如果你覆盖了这个方法，一定要调用 `super()` 来保留规范化。

### 为自定义的用户模型编写一个管理器

你也该为你的用户模型定义一个自定义管理器。如果你的用户模型定义了和默认用户相同的 `username`, `email`, `is_staff`, `is_active`, `is_superuser`, `last_login`, 和 ``date_joined`等字段，你可以安装 `UserManager` ；但是，如果你的用户模型还定义了其他字段，那么就需要定义一个自定义管理器，它扩展了 `BaseUserManager` ，提供了两个额外的方法：

class models.CustomUserManager
  create_user(username_field, password=None, **other_fields)

    `create_user()` 的原型应该接受username字段，加上其他所有必须的字段作为参数。举例，如果你的用户模型使用 `email` 作为用户名字段，`date_of_birth` 字段作为必填字段，那么 `create_user` 应该如下定义：

        def create_user(self, email, date_of_birth, password=None):
            # create user here
            ...

  create_superuser(username_field, password=None, **other_fields)

    `create_superuser()` 的原型应该接受username字段，加上其他所有必须的字段作为参数。举例，如果你的用户模型使用 `email` 作为用户名字段，`date_of_birth` 字段作为必填字段，那么 `create_superuser` 应该如下定义：

        def create_superuser(self, email, date_of_birth, password=None):
            # create superuser here
            ...

对于 `USERNAME_FIELD` 或 `REQUIRED_FIELDS` 中的 `ForeignKey` ，这些方法接收现有实例的:attr:~.ForeignKey.to_field （默认为 `primary_key` ）的值。

`BaseUserManager` 提供以下实用方法：

class models.BaseUserManager
  classmethod normalize_email(email)

    通过降低电子邮件地址的域部分来规范化电子邮件地址。

  get_by_natural_key(username)
  aget_by_natural_key(username)

    Asynchronous version: `aget_by_natural_key()`

    使用 `USERNAME_FIELD` 指定的字段的内容检索用户实例。

    Changed in Django 5.2:

    `aget_by_natural_key()` method was added.

### 扩展Django的默认用户模型

如果您对 Django 的 `User` 模型完全满意，但想要添加一些额外的个人资料信息，您可以子类化 `django.contrib.auth.models.AbstractUser` 并添加您自定义的个人资料字段，尽管我们建议按照 指定自定义用户模型 中描述的方式使用一个单独的模型。`AbstractUser` 提供了默认 `User` 的完整实现作为一个 抽象模型。

### 自定义用户和内建的auth表单

Django的内置 :ref:`forms ` 和 :ref:`views ` 对他们正在使用的用户模型做了一些假设。

以下表单与 `AbstractBaseUser` 的任何子类兼容：

- `AuthenticationForm`: 使用 `USERNAME_FIELD` 指定的username字段。

- `SetPasswordForm`

- `PasswordChangeForm`

- `AdminPasswordChangeForm`

以下表单对用户模型进行了假设，如果满足这些假设，则可以按原样使用:

- `PasswordResetForm` ：假设用户模型有一个字段存储用户的电子邮件地址，其名称由 `get_email_field_name()` 返回（默认为电子邮件），这个方法用来标识用户，以及名为``is_active``的布尔字段，防止非活动用户重置密码。

最后，下面的表单和 `User` 绑定，如果需要和自定义的用户模型一起使用，则需要重写或者扩展。

- `UserCreationForm`

- `UserChangeForm`

如果自定义的用户模型是 `AbstractUser` 的子类，则可以使用下面的方式来扩展表单：

    from django.contrib.auth.forms import UserCreationForm
    from myapp.models import CustomUser

    class CustomUserCreationForm(UserCreationForm):
        class Meta(UserCreationForm.Meta):
            model = CustomUser
            fields = UserCreationForm.Meta.fields + ("custom_field",)

### 自定义用户和 `django.contrib.admin`

如果你希望自定义的用户模型也与管理后台一起使用，那么你的用户模型必须定义一些额外的属性和方法。这些方法允许管理员控制用户对管理后台内容的访问：

class models.CustomUser

is_staff

  如果允许用户有访问 admin 页面就返回 `True`。

is_active

  返回``True``，如果该用户的账号当前是激活状态

has_perm(perm, obj=None):

  如果用户有指定的权限，则返回 `True` 。如果提供了参数 `obj` ，则需要对指定的对象实例进行权限检查。

has_module_perms(app_label):

  如果用户有权限访问指定 app 里的模型，那么返回 `True` 。

你也需要在 admin 文件里注册自定义的用户模型。如果自定义的用户模型扩展了 `django.contrib.auth.models.AbstractUser` ，你可以直接使用Django已有的类 `django.contrib.auth.admin.UserAdmin` 。如果你的用户模型扩展了 `AbstractBaseUser` ，你将需要定义一个自定义的类 `ModelAdmin` 。不管怎样，你都将需要重写任何引用 `django.contrib.auth.models.AbstractUser` 上的字段的定义，这些字段不在你自定义的用户类中。

Note

如果正在使用自定义的 `ModelAdmin` 是 `django.contrib.auth.admin.UserAdmin` 的子类，那么你需要添加自定义字段到 `fieldsets` （用于在编辑用户中使用）和 `add_fieldsets` （用于在创建用户时使用）。比如

    from django.contrib.auth.admin import UserAdmin

    class CustomUserAdmin(UserAdmin):
        ...
        fieldsets = UserAdmin.fieldsets + ((None, {"fields": ["custom_field"]}),)
        add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ["custom_field"]}),)

查看 a full example 来了解更多细节。

### 自定义用户和权限。

为了便于将Django的权限框架引入到你自己的用户类中，Django提供了 `PermissionsMixin` 。这是一个抽象模型，可以包含在用户模型的类层次结构中，为你提供支持Django权限模型所需的所有方法和数据库字段。

`PermissionsMixin` 提供下列方法和属性：

class models.PermissionsMixin
  is_superuser

    布尔值。指定该用户拥有所有权限，而不用一个个开启权限。

  get_user_permissions(obj=None)

    返回用户本身就自带的权限字符串集合。

    如果传入了 `obj` ，则只返回指定对象的用户权限。

  get_group_permissions(obj=None)

    返回用户拥有权限的字符串集合，从用户所属组的权限中获取。

    如果传入 `obj` 参数，则只返回指定对象所属组的权限。

  get_all_permissions(obj=None)

    返回用户拥有权限的字符串集合，同时从用户所属组及用户本身的权限中获取。

    如果传入 ``obj``参数，则只返回指定对象和所属组的权限。

  has_perm(perm, obj=None)

    如果用户具有指定的权限，则返回 `True` ，其中 `perm` 的格式为 `"<app label>.<permission codename>"` (see permissions)。如果 `User.is_active` 和 `is_superuser` 都为 `True`，则这个方法一直返回 `True` 。

    如果传入 `obj` 参数，则这个方法不会检查该模型权限，而只会检查这个出传入对象的权限。

  has_perms(perm_list, obj=None)

    如果用户具有指定权限列表里的每个权限，则返回 `True` ，其中perm的格式为 `"<app label>.<permission codename>"` 。如果 `User.is_active` 和 `is_superuser` 都返回 `True` ，则这个方法一直返回 `True` 。

    如果传入参数 `obj` ，则这个方法不会检查指定的权限列表，只检查指定对象的权限。

  has_module_perms(package_name)

    如果用户拥有所给的 Django app 标签 (the Django app label) 里的任何权限，则会返回 `True` 。如果 `User.is_active` 和 `is_superuser` 都为 `True` ，则该方法一直返回 `True` 。

`PermissionsMixin` 和 `ModelBackend`

如果你没有引入 `PermissionsMixin` ，必须确保没有调用 `ModelBackend` 的权限方法。`ModelBackend` 假定你的用户模型某些字段可用。如果你的用户模型没有提供这些字段，则当你检查权限的时候，会收到数据库错误提示。

### 自定义用户和代理模型

自定义用户模型的一个限制是安装自定义用户模型将破坏任何扩展 `User` 的代理模型。代理模型必须基于具体的基类；通过定义自定义用户模型，你就移除了Django可靠地识别基类的功能。

如果你的项目正在使用代理模型，你必须修改扩展用户模型的代理，或者把代理的行为都合并到 `User` 子类里去。

### 一个完整的例子

这里是一个兼容admin的自定义的用户app的例子。这个用户模型使用 email 地址作为username，并且生日是必填字段；除了用户账户上的 `admin` flag之外，它本身不提供权限检查。除用户创建的表单外，此模型和所有内置的身份验证表单和视图兼容。此例只是说明了大多数组件如何协同工作，不要直接复制到生产环境里。

这段代码将一直存在于 `models.py` 文件中，用于自定义身份验证 app:

    from django.db import models
    from django.contrib.auth.models import BaseUserManager, AbstractBaseUser

    class MyUserManager(BaseUserManager):
        def create_user(self, email, date_of_birth, password=None):
            """
            Creates and saves a User with the given email, date of
            birth and password.
            """
            if not email:
                raise ValueError("Users must have an email address")

            user = self.model(
                email=self.normalize_email(email),
                date_of_birth=date_of_birth,
            )

            user.set_password(password)
            user.save(using=self._db)
            return user

        def create_superuser(self, email, date_of_birth, password=None):
            """
            Creates and saves a superuser with the given email, date of
            birth and password.
            """
            user = self.create_user(
                email,
                password=password,
                date_of_birth=date_of_birth,
            )
            user.is_admin = True
            user.save(using=self._db)
            return user

    class MyUser(AbstractBaseUser):
        email = models.EmailField(
            verbose_name="email address",
            max_length=255,
            unique=True,
        )
        date_of_birth = models.DateField()
        is_active = models.BooleanField(default=True)
        is_admin = models.BooleanField(default=False)

        objects = MyUserManager()

        USERNAME_FIELD = "email"
        REQUIRED_FIELDS = ["date_of_birth"]

        def __str__(self):
            return self.email

        def has_perm(self, perm, obj=None):
            "Does the user have a specific permission?"
            # Simplest possible answer: Yes, always
            return True

        def has_module_perms(self, app_label):
            "Does the user have permissions to view the app `app_label`?"
            # Simplest possible answer: Yes, always
            return True

        @property
        def is_staff(self):
            "Is the user a member of staff?"
            # Simplest possible answer: All admins are staff
            return self.is_admin

然后，在 Django 管理后台里注册这个用户模型，下面这些代码必须在 app 的 `admin.py` 文件里：

    from django import forms
    from django.contrib import admin
    from django.contrib.auth.models import Group
    from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
    from django.contrib.auth.forms import ReadOnlyPasswordHashField
    from django.core.exceptions import ValidationError

    from customauth.models import MyUser

    class UserCreationForm(forms.ModelForm):
        """A form for creating new users. Includes all the required
        fields, plus a repeated password."""

        password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
        password2 = forms.CharField(
            label="Password confirmation", widget=forms.PasswordInput
        )

        class Meta:
            model = MyUser
            fields = ["email", "date_of_birth"]

        def clean_password2(self):
            # Check that the two password entries match
            password1 = self.cleaned_data.get("password1")
            password2 = self.cleaned_data.get("password2")
            if password1 and password2 and password1 != password2:
                raise ValidationError("Passwords don't match")
            return password2

        def save(self, commit=True):
            # Save the provided password in hashed format
            user = super().save(commit=False)
            user.set_password(self.cleaned_data["password1"])
            if commit:
                user.save()
            return user

    class UserChangeForm(forms.ModelForm):
        """A form for updating users. Includes all the fields on
        the user, but replaces the password field with admin's
        disabled password hash display field.
        """

        password = ReadOnlyPasswordHashField()

        class Meta:
            model = MyUser
            fields = ["email", "password", "date_of_birth", "is_active", "is_admin"]

    class UserAdmin(BaseUserAdmin):
        # The forms to add and change user instances
        form = UserChangeForm
        add_form = UserCreationForm

        # The fields to be used in displaying the User model.
        # These override the definitions on the base UserAdmin
        # that reference specific fields on auth.User.
        list_display = ["email", "date_of_birth", "is_admin"]
        list_filter = ["is_admin"]
        fieldsets = [
            (None, {"fields": ["email", "password"]}),
            ("Personal info", {"fields": ["date_of_birth"]}),
            ("Permissions", {"fields": ["is_admin"]}),
        ]
        # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
        # overrides get_fieldsets to use this attribute when creating a user.
        add_fieldsets = [
            (
                None,
                {
                    "classes": ["wide"],
                    "fields": ["email", "date_of_birth", "password1", "password2"],
                },
            ),
        ]
        search_fields = ["email"]
        ordering = ["email"]
        filter_horizontal = []

    # Now register the new UserAdmin...
    admin.site.register(MyUser, UserAdmin)
    # ... and, since we're not using Django's built-in permissions,
    # unregister the Group model from admin.
    admin.site.unregister(Group)

最后，在项目配置文件中的 `AUTH_USER_MODEL` 里指定自定义的用户模型为默认的用户模型。

    AUTH_USER_MODEL = "customauth.MyUser"

#### Adding an async interface

New in Django 5.2.

To optimize performance when called from an async context authentication, backends can implement async versions of each function - `aget_user(user_id)` and `aauthenticate(request, **credentials)`. When an authentication backend extends `BaseBackend` and async versions of these functions are not provided, they will be automatically synthesized with `sync_to_async`. This has performance penalties.

While an async interface is optional, a synchronous interface is always required. There is no automatic synthesis for a synchronous interface if an async interface is implemented.

Django's out-of-the-box authentication backends have native async support. If these native backends are extended take special care to make sure the async versions of modified functions are modified as well.