• Django用户认证(四)自定义认证Customizing authentication


    原文:https://www.cnblogs.com/linxiyue/p/4061044.html

    扩展已有的用户模型Extending the existing User model

    有两种方法来扩展默认的User Model而不用重写自己的模型。如果你不需要改变存储在数据库中的字段,而只是需要改变Model的行为,您可以创建一个基于User的代理Model。允许的行为包括默认的ordering,custom managers, 或者 custom model methods。

    如果你想存储与User有关的信息,可以使用一个OneToOneField字段关联到一个存储额外信息的Model。这一Model通常被称为一个profile model模型,它可以用来存储一些非验证所需的信息。例如,你可以创建一个Model:

    1
    2
    3
    4
    5
    from django.contrib.auth.models import User
     
    class Employee(models.Model):
        user = models.OneToOneField(User)
        department = models.CharField(max_length=100)

    访问:

    1
    2
    >>> u = User.objects.get(username='fsmith')
    >>> freds_department = u.employee.department

    如果需要将profile model的字段添加到admin管理界面的user页面上,需要在应用app的admin.py中定义InlineModelAdmin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    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(UserAdmin):
        inlines = (EmployeeInline, )
     
    # Re-register UserAdmin
    admin.site.unregister(User)
    admin.site.register(User, UserAdmin)

    这些profile models并不特别,只是与User Model有一个OneToOne链接。所以当一个user实例创建时,profile model并不会自动创建。

    重写User模型Substituting a custom User model

    有时候User Model并不适合你的网站,比如你要将email而不是username作为认证标识,这时候就需要重写User Model。

    首先,需要将settings中的默认User Model覆盖:

    1
    AUTH_USER_MODEL = 'myapp.MyUser'

    引用Referencing the User model

    如果AUTH_USER_MODEL已被重设,那当User Model通过ForeignKey或者ManyToManyField访问时,不能直接访问,而是要通过AUTH_USER_MODEL来访问:

    1
    2
    3
    4
    5
    from django.conf import settings
    from django.db import models
     
    class Article(models.Model):
        author = models.ForeignKey(settings.AUTH_USER_MODEL)

    指定Specifying a custom User model

    最简单的定制一个User Model的方法是继承用户类AbstractBaseUser。

    源码:

    一些关键的字段和方法:

    USERNAME_FIELD

    必需的。设置认证标识。设置成标识的字段unique必须为True。

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

    上面的例子中identifier即作为MyUser的认证标识。

    REQUIRED_FIELDS

    字段name组成的列表。当创建superuser时用到的字段。

    1
    2
    3
    4
    5
    6
    class MyUser(AbstractBaseUser):
        ...
        date_of_birth = models.DateField()
        height = models.FloatField()
        ...
        REQUIRED_FIELDS = ['date_of_birth''height']

    列表中不应该包含USERNAME_FIELD字段和password字段。

    is_active

    AbstractBaseUser默认为True。

    get_full_name()

    get_short_name()

    AbstractBaseUser的子类必须定义的两个方法。

    下面为一些AbstractBaseUser的子类可以使用的方法:

    get_username()
    返回USERNAME_FIELD的值.

    is_anonymous()
    返回False。

    is_authenticated()
    返回True。检查一个user是否已登录。

    set_password(raw_password)
    设置密码

    check_password(raw_password)
    检查密码是否正确

    set_unusable_password()

    设置user无密码

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

    get_session_auth_hash()
    返回密码字段的HMAC. Used for Session invalidation on password change.

    还需要为自己的User Model定义一个custom manager。

    class models.CustomUserManager
        create_user(*username_field*, password=None, **other_fields)
    接受username field和required字段来创建用户。例如,如果使用email作为username field, date_of_birth作为required field:

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

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

    创建superuser

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

    create_superuser中的password是必需的。

    扩展内置的表单Custom users and the built-in auth forms

    UserCreationForm

    依赖于User Model. 扩展User时必须重写。

    UserChangeForm

    依赖于User Model. 扩展User时必须重写。

    AuthenticationForm

    Works with任何AbstractBaseUser子类 ,and will adapt to use the field defined in USERNAME_FIELD.

    PasswordResetForm

    Assumes that the user model has an integer primary key, has a field named email that can be used to identify the user, and a boolean field named is_active to prevent password resets for inactive users.

    SetPasswordForm

    Works with 任何AbstractBaseUser子类

    PasswordChangeForm

    Works with任何AbstractBaseUser子类

    AdminPasswordChangeForm

    Works with任何AbstractBaseUser子类。

    定制admin功能Custom users and Admin

    如果想自己定义的User Model能与admin管理系统一起使用,还需要定义一些字段和方法。

    is_staff
    是否允许user访问admin界面

    is_active
    用户是否活跃。

    has_perm(perm, obj=None):
    user是否拥有perm权限。

    has_module_perms(app_label):
    user是否拥有app中的权限

    定制用户和权限Custom users and permissions

    如果要定制User的权限系统,最简单的方法是继承PermissionsMixin

    源码:

    Django内置的User对象就继承了AbstractBaseUser和PermissionsMixin。

    源码:

    现在可以看一个完整的自定义User Model例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    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 get_full_name(self):
            # The user is identified by their email address
            return self.email
     
        def get_short_name(self):
            # The user is identified by their email address
            return self.email
     
        def __str__(self):              # __unicode__ on Python 2
            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

    可以看到manager定义了create_user()和create_superuser()方法,MyUser定义了USERNAME_FIELD,REQUIRED_FIELDS字段和get_full_name(),get_short_name()方法,为了能与admin一起使用,还定义了is_active,is_staff,has_perm(),has_module_perms()

    要在admin中注册自定义的MyUser,还需要在app的admin.py中重写UserCreationForm和UserChangeForm:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    from django import forms
    from django.contrib import admin
    from django.contrib.auth.models import Group
    from django.contrib.auth.admin import UserAdmin
    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(UserCreationForm, self).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 MyUserAdmin(UserAdmin):
        # 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, MyUserAdmin)
    # ... 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:

    1
    AUTH_USER_MODEL = 'customauth.MyUser'

      

  • 相关阅读:
    Java面试题:==运算符与equals方法的区别
    STS或eclipse中导入新项目出现红色感叹号红色叉叉的问题
    SpringBoot+SpringDataJpa快速上手(基本CRUD)
    Java-数组拷贝
    Java-数组拷贝
    最小高度树Java版本(力扣)
    macOS安装minikube
    测试面试LeetCode系列:字符串的左旋转
    测试面试LeetCode系列:按既定顺序创建目标数组
    systemd的文件描述符限制引发的问题
  • 原文地址:https://www.cnblogs.com/chdltanke/p/10365379.html
Copyright © 2020-2023  润新知