在Django中定制认证

Django附带的认证对于大多数情况来说已经足够了,但是你可能有些需求没有被开箱即用的默认值所满足。 要为您的项目定制身份验证,需要了解提供的系统的哪些点是可扩展的或可替换的。 本文档提供了有关如何定制授权系统的详细信息。

Authentication backends provide an extensible system for when a username and password stored with the user model need to be authenticated against a different service than Django’s default.

你可以给你的模型custom permissions,可以通过Django的授权系统进行检查。

You can extend the default User model, or substitute a completely customized model.

其他认证来源

有时可能需要挂接到另一个身份验证源 - 也就是另一个用户名和密码来源或身份验证方法。

例如,您的公司可能已经有一个LDAP设置,为每个员工存储一个用户名和密码。 如果用户在LDAP和基于Django的应用程序中有单独的帐户,那么对于网络管理员和用户本身来说都是一件麻烦事。

所以,为了处理这样的情况,Django认证系统可以让你插入其他认证源。 你可以重写Django默认的基于数据库的方案,或者你可以使用默认的系统与其他系统一起使用。

请参阅authentication backend reference了解有关Django中包含的身份验证后端的信息。

指定认证后端

Django在后台维护一个“身份验证后端”列表,用于检查身份验证。 当有人调用django.contrib.auth.authenticate()时,如How to log a user in中记录用户 - Django尝试在所有身份验证后端进行身份验证。 如果第一个认证方法失败,Django会尝试第二个认证方法,依此类推,直到所有的后端都被尝试。

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

默认情况下,AUTHENTICATION_BACKENDS设置为:

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

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

AUTHENTICATION_BACKENDS的顺序很重要,所以如果在多个后端使用相同的用户名和密码,Django将在第一次正确匹配时停止处理。

如果后端产生一个PermissionDenied异常,认证立即失败。 Django不会检查后面的后端。

注意

一旦用户进行了认证,Django将存储哪个后端用于在用户会话中对用户进行身份验证,并且只要需要访问当前已通过身份验证的用户,就会在该会话期间重新使用相同的后端。 这实际上意味着验证源以每个会话为单位进行缓存,所以如果您更改AUTHENTICATION_BACKENDS,则需要清除会话数据,如果您需要强制用户使用不同的方法重新验证。 一个简单的方法就是执行Session.objects.all().delete()

编写认证后端

An authentication backend is a class that implements two required methods: get_user(user_id) and authenticate(request, **credentials), as well as a set of optional permission related authorization methods.

get_user方法需要一个user_id - 可以是用户名,数据库ID或其他类型,但必须是用户对象的主键,并返回一个用户对象。

authenticate方法将request参数和凭证作为关键字参数。 大多数情况下,它看起来像这样:

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

但它也可以验证令牌,如下所示:

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

Either way, authenticate() should check the credentials it gets and return a user object that matches those credentials if the credentials are valid. 如果他们无效,应该返回None

request is an HttpRequest and may be None if it wasn’t provided to authenticate() (which passes it on to the backend).

Django管理员与Django User object紧密耦合。 The best way to deal with this is to create a Django User object for each user that exists for your backend (e.g., in your LDAP directory, your external SQL database, etc.) You can either write a script to do this in advance, or your authenticate method can do it the first time a user logs in.

以下是一个示例后端,它会根据settings.py文件中定义的用户名和密码变量进行身份验证,并在用户首次验证时创建一个Django User对象:

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

class SettingsBackend:
    """
    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)
                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
在Django 1.11中更改:

request参数已被添加到authenticate(),并且支持不接受它的后端将在Django 2.1中被删除。

处理自定义后端中的授权

自定义身份验证后端可以提供自己的权限。

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

赋予用户的权限将是所有后端返回的所有权限的超集。 也就是说,Django向用户授予任何一个后端授予的权限。

如果后端在has_perm()has_module_perms()中引发了PermissionDenied异常,授权将立即失败,Django不会检查后端随后。

上面简单的后端可以很简单地实现魔术管理员的权限:

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

在上面的例子中,这给予授予访问权限的用户的完全权限。 请注意,除了给与关联的django.contrib.auth.models.User函数相同的参数之外,后端auth函数还会将用户对象(可能是匿名用户)作为参数。

完整的授权实现可以在django / contrib / auth / backends.pyModelBackend类中找到,它是默认的后端,并且查询auth_permission If you wish to provide custom behavior for only part of the backend API, you can take advantage of Python inheritance and subclass ModelBackend instead of implementing the complete API in a custom backend.

匿名用户授权

匿名用户是没有认证的用户,即他们没有提供有效的认证信息。 但是,这并不一定意味着他们无权做任何事情。 在最基本的层面上,大多数网站授权匿名用户浏览大部分网站,并且许多网站允许匿名发布评论等。

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

非活动用户授权

非活动用户的is_active字段设置为False ModelBackendRemoteUserBackend身份验证后端禁止这些用户进行身份验证。 如果自定义用户模型没有is_active字段,则将允许所有用户进行身份验证。

如果要允许非活动用户进行身份验证,可以使用AllowAllUsersModelBackendAllowAllUsersRemoteUserBackend

对权限系统中的匿名用户的支持允许匿名用户有权执行某些操作,而不活动的经过身份验证的用户则不能这样做。

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

处理对象权限

Django的权限框架为对象权限奠定了基础,但在核心中没有实现。 这意味着检查对象权限将总是返回False或一个空列表(取决于执行的检查)。 身份验证后端将为每个对象相关的授权方法接收关键字参数objuser_obj,并可以根据需要返回对象级权限。

自定义权限

要为给定模型对象创建自定义权限,请使用permissions model Meta attribute

本示例任务模型创建三个自定义权限,即用户可以或不可以对Task实例执行的操作,这些操作对于您的应用程序是特定的:

class Task(models.Model):
    ...
    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
            ("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.view_task')

扩展现有的User模型

有两种方法可以扩展默认的User模型,而不用替换自己的模型。 如果您需要的更改是纯粹的行为,并且不需要对存储在数据库中的内容进行任何更改,则可以基于User创建proxy model 这允许代理模型提供的任何功能,包括默认排序,自定义管理器或自定义模型方法。

如果您希望存储与User相关的信息,则可以使用OneToOneField来包含字段的模型以获取更多信息。 这种一对一模式通常被称为配置文件模型,因为它可能存储有关站点用户的非auth相关信息。 例如,您可以创建一个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

To add a profile model’s fields to the user page in the admin, define an InlineModelAdmin (for this example, we’ll use a StackedInline) in your app’s admin.py and add it to a UserAdmin class which is registered with the User class:

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)

这些配置文件模型并不是什么特别的 - 它们只是恰好与用户模型具有一对一连接的Django模型。 因此,在创建用户时不会自动创建,但可以根据需要使用django.db.models.signals.post_save来创建或更新相关模型。

使用相关模型会产生额外的查询或连接来检索相关数据。 根据您的需要,包含相关字段的自定义用户模型可能是您更好的选择,但是,与项目的应用程序中的默认用户模型的现有关系可能会证明额外的数据库负载。

用自定义的User模型来代替

某些类型的项目可能具有身份验证要求,而Django的内置User模型并不总是适合的。 例如,在一些网站上,使用电子邮件地址作为您的身份标记而不是用户名更有意义。

Django允许您通过提供引用自定义模型的AUTH_USER_MODEL设置的值来覆盖默认用户模型:

AUTH_USER_MODEL = 'myapp.MyUser'

该虚线对描述了Django应用程序的名称(它必须位于INSTALLED_APPS中),以及您希望用作用户模型的Django模型的名称。

在开始项目时使用自定义用户模型

如果您正在开始一个新项目,强烈建议设置一个自定义用户模型,即使默认的User模型对您来说已经足够了。 此模型的行为与默认用户模型的行为相同,但如果需要,您可以在将来自定义它:

from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    pass

不要忘记指向AUTH_USER_MODEL 在创建任何迁移之前或首次运行manage.py 迁移

另外,在应用程序的admin.py中注册模型:

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

admin.site.register(User, UserAdmin)

更改为自定义用户模型mid-project

例如,在创建数据库表之后更改AUTH_USER_MODEL显得更加困难,因为它会影响外键和多对多关系。

此更改不能自动完成,并需要手动修复架构,从旧用户表移动数据,并可能需要手动重新应用一些迁移。 请参阅#25313了解步骤概述。

由于Django对可交换模型的动态依赖特性的限制,必须在其应用程序的第一次迁移(通常称为0001_initial)中创建由AUTH_USER_MODEL引用的模型。否则,你会有依赖性问题。

In addition, you may run into a CircularDependencyError when running your migrations as Django won’t be able to automatically break the dependency loop due to the dynamic dependency. 如果你看到这个错误,你应该通过将你的用户模型所依赖的模型移动到第二个移植中来打破这个循环。 (You can try making two normal models that have a ForeignKey to each other and seeing how makemigrations resolves that circular dependency if you want to see how it’s usually done.)

可重复使用的应用程序和AUTH_USER_MODEL

可重复使用的应用程序不应该实现自定义用户模型。 一个项目可能会使用许多应用程序,而两个实现了自定义用户模型的可重用应用程序不能一起使用。 如果您需要在应用程序中存储每个用户的信息,请按照以下说明使用ForeignKeyOneToOneFieldsettings.AUTH_USER_MODEL

引用User模型

如果直接引用User(例如,通过在外键中引用),则您的代码在AUTH_USER_MODEL设置被更改为不同用户模型。

get_user_model()[source]

不要直接引用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)

Generally speaking, it’s easiest to refer to the user model with the AUTH_USER_MODEL setting in code that’s executed at import time, however, it’s also possible to call get_user_model() while Django is importing models, so you could use 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(**kwargs):
    if kwargs['setting'] == 'AUTH_USER_MODEL':
        apps.clear_cache()
        from myapp import some_module
        some_module.UserModel = get_user_model()
在Django 1.11中更改:

添加了在导入时调用get_user_model()的功能。

指定自定义用户模型

模型设计的考虑

在处理与自定义用户模型中的身份验证不直接相关的信息之前请仔细考虑

将应用程序特定的用户信息存储在与用户模型有关系的模型中可能会更好。 这允许每个应用程序指定自己的用户数据要求,而不会冒险与其他应用程序的冲突。 另一方面,检索这些相关信息的查询将涉及数据库连接,这可能会影响性能。

Django期望您的自定义用户模型满足一些最低要求。

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

构建兼容的自定义用户模型的最简单方法是从AbstractBaseUser继承。 AbstractBaseUser provides the core implementation of a user model, including hashed passwords and tokenized password resets. 然后你必须提供一些关键的实现细节:

楷模。 CustomUser T0> ¶ T1>
USERNAME_FIELD T0> ¶ T1>

描述用作唯一标识符的用户模型上字段的名称的字符串。 这通常是某种用户名,但也可以是电子邮件地址或任何其他唯一标识符。 除非您使用可以支持非唯一用户名的自定义身份验证后端,否则字段必须是唯一的(即在其定义中设置unique=True)。

在以下示例中,字段identifier用作标识字段:

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

USERNAME_FIELD now supports ForeignKeys. Since there is no way to pass model instances during the createsuperuser prompt, expect the user to enter the value of to_field value (the primary_key by default) of an existing instance.

EMAIL_FIELD T0> ¶ T1>
Django 1.11新增功能

描述User模型中电子邮件字段名称的字符串。 该值由get_email_field_name()返回。

REQUIRED_FIELDS T0> ¶ T1>

通过createsuperuser管理命令创建用户时将会提示的字段名称列表。 系统将提示用户为每个字段提供一个值。 它必须包含blankFalse或未定义的任何字段,并且可能包含交互式创建用户时要提示的其他字段。 REQUIRED_FIELDS has no effect in other parts of Django, like creating a user in the admin.

例如,以下是用户模型的部分定义,它定义了两个必填字段 - 出生日期和身高:

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

注意

REQUIRED_FIELDS must contain all required fields on your user model, but should not contain the USERNAME_FIELD or password as these fields will always be prompted for.

REQUIRED_FIELDS now supports ForeignKeys. Since there is no way to pass model instances during the createsuperuser prompt, expect the user to enter the value of to_field value (the primary_key by default) of an existing instance.

IS_ACTIVE T0> ¶ T1>

指示用户是否被视为“活动”的布尔属性。 此属性作为AbstractBaseUser默认为True的属性提供。 你如何选择实现它将取决于你选择的auth后端的细节。 See the documentation of the is_active attribute on the built-in user model for details.

get_full_name T0>()¶ T1>

可选的。 用户的正式标识符,如全名。 如果已实施,则会在django.contrib.admin中的对象历史记录中显示用户名。

get_short_name T0>()¶ T1>

可选的。 用户的简短的非正式标识符,例如他们的名字。 如果已实施,则会将问候语中的用户名替换为django.contrib.admin标题中的用户。

在Django 2.0中更改:

在较早的版本中,实现get_short_name()get_full_name()需要子类作为AbstractBaseUser具有引发NotImplementedError

导入AbstractBaseUser

AbstractBaseUser and BaseUserManager are importable from django.contrib.auth.base_user so that they can be imported without including django.contrib.auth in INSTALLED_APPS.

以下属性和方法可用于AbstractBaseUser的任何子类:

楷模。 AbstractBaseUser T0> ¶ T1>
get_username T0>()¶ T1>

返回USERNAME_FIELD指定的字段的值。

清洁 T0>()¶ T1>

通过调用normalize_username()来规范用户名。 如果您重写此方法,请确保调用super()以保持规范化。

类方法 get_email_field_name T0>()¶ T1>
Django 1.11新增功能

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

类方法 normalize_username T0>(用户名 T1>)¶ T2>

将NFKC Unicode规范化应用于用户名,以便将具有不同Unicode代码点的视觉上相同的字符视为相同。

is_authenticated T0> ¶ T1>

总是True的只读属性(与AnonymousUser.is_authenticated永远是False相反)。 这是一种判断用户是否被认证的方法。 这并不意味着任何权限,也不会检查用户是否处于活动状态或具有有效的会话。 即使你通常会在request.user上检查这个属性,以确定它是否已经被AuthenticationMiddleware(代表当前登录的用户)填充,你应该知道对于任何User实例,这个属性是True

is_anonymous T0> ¶ T1>

总是False的只读属性。 这是区分UserAnonymousUser对象的一种方法。 通常,您应该更喜欢使用is_authenticated来使用此属性。

set_password T0>( raw_password T1>)¶ T2>

将用户的密码设置为给定的原始字符串,注意密码散列。 不保存AbstractBaseUser对象。

当raw_password为None时,密码将被设置为不可用的密码,就像使用set_unusable_password()一样。

check_password T0>( raw_password T1>)¶ T2>

如果给定的原始字符串是用户的正确密码,则返回True (这在做比较时需要密码哈希处理。)

set_unusable_password T0>()¶ T1>

标记用户没有设置密码。 这与密码的空白字符串不一样。 这个用户的check_password()永远不会返回True 不保存AbstractBaseUser对象。

如果您的应用程序的身份验证是针对现有的外部源(例如LDAP目录)进行的,则可能需要此操作。

has_usable_password T0>()¶ T1>

如果为此用户调用了set_unusable_password(),则返回False

get_session_auth_hash T0>()¶ T1>

返回密码字段的HMAC。 用于密码更改的Session invalidation on password change

AbstractUser子类AbstractBaseUser

楷模。 AbstractUser T0> ¶ T1>
清洁 T0>()¶ T1>
Django 1.11新增功能

通过调用BaseUserManager.normalize_email()来规范化电子邮件。 如果您重写此方法,请确保调用super()以保持规范化。

您还应该为您的用户模型定义一个自定义管理器。 If your user model defines username, email, is_staff, is_active, is_superuser, last_login, and date_joined fields the same as Django’s default user, you can just install Django’s UserManager; however, if your user model defines different fields, you’ll need to define a custom manager that extends BaseUserManager providing two additional methods:

楷模。 CustomUserManager T0> ¶ T1>
create_user* username_field *password = None** other_fields

create_user()的原型应接受用户名字段以及所有必填字段作为参数。 例如,如果您的用户模型使用email作为用户名字段,并将date_of_birth作为必填字段,则应将create_user定义为:

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

create_superuser()的原型应接受用户名字段以及所有必填字段作为参数。 例如,如果您的用户模型使用email作为用户名字段,并将date_of_birth作为必填字段,则应将create_superuser定义为:

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

create_user()不同,create_superuser() 必须要求调用者提供密码。

BaseUserManager provides the following utility methods:

楷模。 BaseUserManager T0> ¶ T1>
类方法 normalize_email T0>(电子邮件 T1>)¶ T2>

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

get_by_natural_key T0>(用户名 T1>)¶ T2>

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

make_random_passwordlength = 10allowed_chars ='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'

返回给定长度的随机密码,并给出允许的字符串。 请注意,allowed_chars的默认值不包含可能导致用户混淆的字母,其中包括:

  • i, l, I, and 1 (lowercase letter i, lowercase letter L, uppercase letter i, and the number one)
  • o, O, and 0 (lowercase letter o, uppercase letter o, and zero)

扩展Django的默认User

如果您对Django的User模型完全满意,并且只想添加一些额外的配置文件信息,那么您可以将django.contrib.auth.models.AbstractUser您的自定义配置文件字段,尽管我们建议使用Specifying a custom user model的“模型设计注意事项”中所述的单独模型。 AbstractUser provides the full implementation of the default User as an abstract model.

自定义用户和内置的认证表单

Django内置的formsviews对于他们正在使用的用户模型做了某些假设。

以下形式与AbstractBaseUser的任何子类兼容:

以下形式对用户模型做出假设,如果满足这些假设,可以按原样使用:

  • PasswordResetForm: Assumes that the user model has a field that stores the user’s email address with the name returned by get_email_field_name() (email by default) that can be used to identify the user and a boolean field named is_active to prevent password resets for inactive users.

最后,以下表单与User绑定,需要重写或扩展以使用自定义用户模型:

如果您的自定义用户模型是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

如果您希望自定义用户模型也可以与管理员一起工作,那么您的用户模型必须定义一些额外的属性和方法。 这些方法允许管理员控制用户对管理内容的访问:

楷模。CustomUser
is_staff T0> ¶ T1>

如果用户被允许访问管理站点,则返回True

IS_ACTIVE T0> ¶ T1>

如果用户帐户当前处于活动状态,则返回True

has_perm(perm,obj = None):

如果用户具有指定的权限,则返回True 如果提供了obj,则需要根据特定的对象实例来检查权限。

has_module_perms(app_label):

如果用户有权访问给定应用程序中的模型,则返回True

您还需要向管理员注册您的自定义用户模型。 如果您的自定义用户模型扩展了django.contrib.auth.models.AbstractUser,则可以使用Django的现有django.contrib.auth.admin.UserAdmin类。 但是,如果您的用户模型扩展AbstractBaseUser,则需要定义一个自定义ModelAdmin类。 可以将默认的django.contrib.auth.admin.UserAdmin;但是,您需要覆盖任何不在您的自定义用户类中的引用django.contrib.auth.models.AbstractUser上的字段的定义。

自定义用户和权限

为了使Django的权限框架容易包含到你自己的用户类中,Django提供了PermissionsMixin 这是一个抽象模型,可以包含在用户模型的类层次结构中,为您提供支持Django许可模型所需的所有方法和数据库字段。

PermissionsMixin provides the following methods and attributes:

楷模。 PermissionsMixin T0> ¶ T1>
is_superuser T0> ¶ T1>

布尔。 指定该用户具有所有权限而不明确分配它们。

get_group_permissions T0>( OBJ =无 T1>)¶ T2>

通过他们的组返回一组用户拥有的权限字符串。

如果传入obj,则只返回此特定对象的组权限。

get_all_permissions T0>( OBJ =无 T1>)¶ T2>

通过组和用户权限返回用户拥有的一组权限字符串。

如果传入obj,则只会返回此特定对象的权限。

has_permpermobj = None

如果用户具有指定权限,则返回True,其中perm的格式为“< app 标签&gt ;. <权限 codename>“(请参阅permissions)。 如果用户处于非活动状态,则此方法将始终返回False

如果传入obj,则此方法不会检查模型的权限,而是检查此特定对象的权限。

has_permsperm_listobj = None

Returns True if the user has each of the specified permissions, where each perm is in the format "<app label>.<permission codename>". 如果用户处于非活动状态,则此方法将始终返回False

如果传入obj,则此方法将不检查模型的权限,而是检查特定对象的权限。

has_module_perms T0>(程序包 T1>)¶ T2>

如果用户在给定包(Django应用程序标签)中有任何权限,则返回True 如果用户处于非活动状态,则此方法将始终返回False

PermissionsMixin and ModelBackend

如果您不包含PermissionsMixin,则必须确保不要调用ModelBackend上的权限方法。 ModelBackend assumes that certain fields are available on your user model. 如果您的用户模型不提供这些字段,那么在检查权限时将会收到数据库错误。

自定义用户和代理模型

自定义用户模型的一个限制是安装自定义用户模型将会破坏任何扩展User的代理模型。 代理模型必须基于具体的基类;通过定义一个自定义的用户模型,可以移除Django可靠地识别基类的能力。

如果您的项目使用代理模型,则必须修改代理以扩展项目中正在使用的用户模型,或者将代理的行为合并到您的User子类中。

一个完整的例子

以下是符合管理员要求的自定义用户应用的示例。 此用户模型使用电子邮件地址作为用户名,并具有必需的出生日期;它不提供许可检查,除了用户帐户上的一个简单的admin标志之外。 除了用户创建表单之外,该模型将与所有内置的认证表单和视图兼容。 这个例子说明了大多数组件是如何协同工作的,但并不打算直接复制到项目中以供生产使用。

这段代码将全部存在于一个自定义认证应用的models.py文件中:

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):
        """
        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的管理员注册这个自定义用户模型,应用程序的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 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 forms.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
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


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)

最后,使用settings.py中的AUTH_USER_MODEL设置将自定义模型指定为项目的默认用户模型:

AUTH_USER_MODEL = 'customauth.MyUser'