Django自定义用户认证系统Customizing authentication

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

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

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

from django.contrib.auth.models import User

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

访问:

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

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

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覆盖:

AUTH_USER_MODEL = ‘myapp.MyUser‘

引用Referencing the User model

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

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。

源码:

@python_2_unicode_compatible
class AbstractBaseUser(models.Model):
    password = models.CharField(_(‘password‘), max_length=128)
    last_login = models.DateTimeField(_(‘last login‘), blank=True, null=True)

    is_active = True

    REQUIRED_FIELDS = []

    class Meta:
        abstract = True

    def get_username(self):
        "Return the identifying username for this User"
        return getattr(self, self.USERNAME_FIELD)

    def __str__(self):
        return self.get_username()

    def natural_key(self):
        return (self.get_username(),)

    def is_anonymous(self):
        """
        Always returns False. This is a way of comparing User objects to
        anonymous users.
        """
        return False

    def is_authenticated(self):
        """
        Always return True. This is a way to tell if the user has been
        authenticated in templates.
        """
        return True

    def set_password(self, raw_password):
        self.password = make_password(raw_password)

    def check_password(self, raw_password):
        """
        Returns a boolean of whether the raw_password was correct. Handles
        hashing formats behind the scenes.
        """
        def setter(raw_password):
            self.set_password(raw_password)
            self.save(update_fields=["password"])
        return check_password(raw_password, self.password, setter)

    def set_unusable_password(self):
        # Sets a value that will never be a valid hash
        self.password = make_password(None)

    def has_usable_password(self):
        return is_password_usable(self.password)

    def get_full_name(self):
        raise NotImplementedError(‘subclasses of AbstractBaseUser must provide a get_full_name() method‘)

    def get_short_name(self):
        raise NotImplementedError(‘subclasses of AbstractBaseUser must provide a get_short_name() method.‘)

    def get_session_auth_hash(self):
        """
        Returns an HMAC of the password field.
        """
        key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
        return salted_hmac(key_salt, self.password).hexdigest()

一些关键的字段和方法:

USERNAME_FIELD

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

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

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

REQUIRED_FIELDS

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

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:

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

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

创建superuser

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

源码:

class PermissionsMixin(models.Model):
    """
    A mixin class that adds the fields and methods necessary to support
    Django‘s Group and Permission model using the ModelBackend.
    """
    is_superuser = models.BooleanField(_(‘superuser status‘), default=False,
        help_text=_(‘Designates that this user has all permissions without ‘
                    ‘explicitly assigning them.‘))
    groups = models.ManyToManyField(Group, verbose_name=_(‘groups‘),
        blank=True, help_text=_(‘The groups this user belongs to. A user will ‘
                                ‘get all permissions granted to each of ‘
                                ‘their groups.‘),
        related_name="user_set", related_query_name="user")
    user_permissions = models.ManyToManyField(Permission,
        verbose_name=_(‘user permissions‘), blank=True,
        help_text=_(‘Specific permissions for this user.‘),
        related_name="user_set", related_query_name="user")

    class Meta:
        abstract = True

    def get_group_permissions(self, obj=None):
        """
        Returns a list of permission strings that this user has through their
        groups. This method queries all available auth backends. If an object
        is passed in, only permissions matching this object are returned.
        """
        permissions = set()
        for backend in auth.get_backends():
            if hasattr(backend, "get_group_permissions"):
                permissions.update(backend.get_group_permissions(self, obj))
        return permissions

    def get_all_permissions(self, obj=None):
        return _user_get_all_permissions(self, obj)

    def has_perm(self, perm, obj=None):
        """
        Returns True if the user has the specified permission. This method
        queries all available auth backends, but returns immediately if any
        backend returns True. Thus, a user who has permission from a single
        auth backend is assumed to have permission in general. If an object is
        provided, permissions for this specific object are checked.
        """

        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        # Otherwise we need to check the backends.
        return _user_has_perm(self, perm, obj)

    def has_perms(self, perm_list, obj=None):
        """
        Returns True if the user has each of the specified permissions. If
        object is passed, it checks if the user has all required perms for this
        object.
        """
        for perm in perm_list:
            if not self.has_perm(perm, obj):
                return False
        return True

    def has_module_perms(self, app_label):
        """
        Returns True if the user has any permissions in the given app label.
        Uses pretty much the same logic as has_perm, above.
        """
        # Active superusers have all permissions.
        if self.is_active and self.is_superuser:
            return True

        return _user_has_module_perms(self, app_label)

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

源码:

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.
    Username, password and email are required. Other fields are optional.
    """
    username = models.CharField(_(‘username‘), max_length=30, unique=True,
        help_text=_(‘Required. 30 characters or fewer. Letters, digits and ‘
                    ‘@/./+/-/_ only.‘),
        validators=[
            validators.RegexValidator(r‘^[\[email protected]+-]+$‘,
                                      _(‘Enter a valid username. ‘
                                        ‘This value may contain only letters, numbers ‘
                                        ‘and @/./+/-/_ characters.‘), ‘invalid‘),
        ],
        error_messages={
            ‘unique‘: _("A user with that username already exists."),
        })
    first_name = models.CharField(_(‘first name‘), max_length=30, blank=True)
    last_name = models.CharField(_(‘last name‘), max_length=30, blank=True)
    email = models.EmailField(_(‘email address‘), blank=True)
    is_staff = models.BooleanField(_(‘staff status‘), default=False,
        help_text=_(‘Designates whether the user can log into this admin ‘
                    ‘site.‘))
    is_active = models.BooleanField(_(‘active‘), default=True,
        help_text=_(‘Designates whether this user should be treated as ‘
                    ‘active. Unselect this instead of deleting accounts.‘))
    date_joined = models.DateTimeField(_(‘date joined‘), default=timezone.now)

    objects = UserManager()

    USERNAME_FIELD = ‘username‘
    REQUIRED_FIELDS = [‘email‘]

    class Meta:
        verbose_name = _(‘user‘)
        verbose_name_plural = _(‘users‘)
        abstract = True

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = ‘%s %s‘ % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.
    Username, password and email are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = ‘AUTH_USER_MODEL‘

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

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:

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:

AUTH_USER_MODEL = ‘customauth.MyUser‘

  

时间: 2024-08-22 22:51:01

Django自定义用户认证系统Customizing authentication的相关文章

Django用户认证系统 authentication system

Django集成了用户登陆/验证/登出的模块,可以直接拿来使用,也可以进行定制,使用起来非常方便. 一,创建Django工程 $ django-admin.py startproject mysite 工程目录结构如下 [email protected]:~/Study/django$ tree mysite/ mysite/ ├── manage.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py 1 di

Django 自定义用户认证

Django 自定义用户认证 Django附带的认证对于大多数常见情况来说已经足够了,但是如何在 Django 中使用自定义的数据表进行用户认证,有一种较为笨蛋的办法就是自定义好数据表后,使用OnetoOne来跟 Django 的表进行关联,类似于这样: from django.contrib.auth.models import User class UserProfile(models.Model): """ 用户账号表 """ user =

Django自定义用户认证

自定义一个用户认证 详细参考官方文档: https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#django.contrib.auth.models.PermissionsMixin.has_perms 一.创建用户的表 UserProfile():存放用户信息的表 UserProfileManager(): 用户创建用户的类方法 1.生成一个单独的Moel文件专门用来存放用户表 #!/usr/bin/env python #

django(权限、认证)系统—— 基于Authentication backends定制

在这篇文章中,我们进行最后关于DjangoPermission系统的探讨,来谈谈关于Permission系统后台接口和扩展后台接口的开发. Django实现的这套permission体系,在底层被抽象为authentication backends.Django auth backends的默认的内置的实现,就是我们前4篇blog所描述的,基于三个数据库模型User,Permission,Group.在实际开发中,很有可能我们的用户标示或者是密码并非存在于User表中,比如说存放在LDAP中,再

西游之路——python全栈——自定义用户认证

django自定义用户认证(使用自定义的UserProfile,而不是django自带的),就需要(django要求)将为UserProfile单独创建一个app,这个app啥也不干,就是为UserProfile而生的; 这里我们创建一个app,名字叫做custom_auth,事实上,我们只需要对其中的models文件和admin.py做操作就可以了; 第一步:  创建user model 下面是models.py文件的内容: 1 from django.utils.safestring imp

Django Authentication 用户认证系统

一. Django的认证系统 Django自带一个用户认证系统,用于处理用户账户.群组.许可和基于cookie的用户会话. 1.1 概览 Django的认证系统包含了身份验证和权限管理两部分.简单地说,身份验证用于核实某个用户是否是合法用户,权限管理则是决定一个合法用户具有哪些权限.这里,"身份验证"这个词同时代指上面两部分的含义. 系统主要包括: 用户 许可 组 可配置的密码哈希系统 用于用户登录或者限制访问的表单和视图工具 可插拔的后端 类似下面的问题,请使用第三方包: 密码强度检

Django实战之用户认证系统

详情请交流  QQ  709639943 00.Python 从入门到精通 78节.2000多分钟.36小时的高质量.精品.1080P高清视频教程!包括标准库.socket网络编程.多线程.多进程和协程. 00.Django实战之用户认证系统 00.Django实战之企业级博客 00.深入浅出Netty源码剖析 00.NIO+Netty5各种RPC架构实战演练 00.JMeter 深入进阶性能测试体系 各领域企业实战 00.30天搞定大数据爬虫项目 00.零基础实战机器学学习 00.企业级实战

django用户认证系统——登录4

用户已经能够在我们的网站注册了,注册就是为了登录,接下来我们为用户提供登录功能.和注册不同的是,Django 已经为我们写好了登录功能的全部代码,我们不必像之前处理注册流程那样费劲了.只需几分钟的简单配置,就可为用户提供登录功能.接下来就来看看如何使用内置的登录功能. 引入内置的 URL 模型 Django 内置的登录.修改密码.找回密码等视图函数对应的 URL 模式位于 django.contrib.auth.urls.py 中,首先在工程的 urls.py 文件里包含这些 URL 模式.打开

自定义用户认证(继承django的)

1.在app下创建一个自己用户认证文件,文件名随意,记得为.py文件 2.编辑该userauth.py文件 1 #!/usr/bin/env python 2 #coding:utf-8 3 from django.db import models 4 from django.contrib.auth.models import ( 5 BaseUserManager, AbstractBaseUser 6 ) 7 import django 8 9 class UserManager(Base