本文介绍Django的用户认证系统。我们将了解认证系统是如何工作的,以及用户模型以及如何自定义:通过扩展用户模型或创建配置文件,并介绍如何通过权限和用户组来控制用户访问。
DJANGO认证系统
每个Django项目都带了一个全功能的且可伸缩的认证模块,我们查看settings.py的 INSTALLED_APPS节点, 能看到安装了 ‘django.contrib.auth’ 应用,利用该应用我们可以认证用户、登录、登出、修改密码等等,这个应用有很多模块比如permission、group等,很明显我们需要用数据表来保存这些模块数据。我们查看数据库能看到很多数据表,比如权限、组、用户以及他们之前的联系。查看auth_user表,该表有很多字段如用户名密码等,其中有is_superuser表示是否是管理员,is_staff 表示是否能访问管理区域等等,可以给这个数据表添加列,下节介绍。
查看settings.py, 我们看到还有一段 MIDDLEWARE 配置 ,如下:
MIDDLEWARE = [
‘debug_toolbar.middleware.DebugToolbarMiddleware’,
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]
中间件是一个函数,中间件可以获取request, 进行处理并传给下一个中间件或返回响应。在Django中,当收到一个request时,一般会交给views处理,但在交给views处理之前,就按顺序经过MIDDLEWARE 配置的各个函数处理,这些处理函数可以加上东西交给下一个中间件函数,也可以返回响应。如果返回响应,则不交给下一个函数了。
中间件函数有一个是AuthenticationMiddleware,它获取request,然后设置user属性,这样在views中,我们就可以直接使用request.user了。
自定义用户模型
有两种方法自定义用户模型,一种是扩展用户模型,另一种是使用用户配置文件。
扩展方法是继承原生的User模型,添加的字段都写在原User模型对应的数据表上,扩展的字段一般必须与用户验证相关。其他情况都建议建立用户配置文件,用户配置文件使用组合方式来使用用户模型,数据与原来的User表分离,这样不同的应用可以建立不同的用户配置文件,比如sales应用使用Customer,hr使用Employee,而Training则使用Student等。
扩展用户模型
查看user表,我们看到username是有唯一约束的,但email没有,也就是说不同用户可以用同一个email. 或许之后某个时候,我们希望可以用email登录。我们来修改一下email保证不能重复。这个字段属于认证范畴,可以通过扩展User模型方法来实现。
扩展类放哪里?放在store肯定不合适,因为该应用跟认证没有关系。我们放在另外一个应用里!之前有使用过一个store_custom应用,我们修改成core并把扩展User的类放那儿!
重命名store_custom文件夹为core,然后在app.py修改store_custom为core.
from django.apps import AppConfig
class CoreConfig(AppConfig):
default_auto_field = ‘django.db.models.BigAutoField’
name = ‘core’
再在settings.py中的INSTALLED_APPS修改store_custom为core.
修改完毕后,我们开始添加继承类,core–models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
email = models.EmailField(unique=True)
新建User继承于AbstractUser,并重写email字段,设置 unique=True.
再到 settings.py, 添加一行,表示使用core.User为认证用户模型。
AUTH_USER_MODEL = ‘core.User’
我们得到一个提示,大致意思是like应用已使用了User,我们到like–models.py,修改:
from django.conf import settings
…
class LikedItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
…
我们使用settings.py里配置的AUTH_USER_MODEL指定的User类作为 LikeItem的外键,这样就解除了耦合。但在调用migrate时出错,原因是在中间使用User的扩展类总有很多问题。
最佳实践就是一开始就新建一个自定义User类,即便之后用不到也没事。像这样:
class User(AbstractUser):
pass
以后需要添加具体代码的话就添加相应的代码。
但现在我们就是在中途想添加自定义User类,该如何做呢?重建数据库吧(生产环境当然不能这样,需要妥善处理数据)
DROP DATABASE storefront2; CREATE DATABASE storefront2; 然后再进行迁移,并重新创建超级用户用于登录后台。 python manage.py migrate python manage.py createsuperuser 再登录管理台,发现USER模型是没有注册的,我们需要手动注册。 ...
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import User
@admin.register(User)
class UserAdmin(BaseUserAdmin):
pass
再刷新下,就在后台管理里看到User了。
现在尝试添加用户,发现并没有要求提供email, 我们按BaseUserAdmin的实现方法修改下core.admin.UserAdmin:
@admin.register(User)
class UserAdmin(BaseUserAdmin):
add_fieldsets = (
(None, {
‘classes’: (‘wide’,),
‘fields’: (‘username’, ‘password1’, ‘password2′, ’email’, ‘first_name’, ‘last_name’),
}),
)
添加add_fieldsets属性,fields添加email, 顺便也添加first_name和last_name。再尝试添加就能添加相应字段了。
回到数据库查看,发现user表已不在auth应用下了,而在core应用下,为 core_user表了。
创建用户配置文件
用户配置文件的话,就是跟User建立关系的一个模型,比如这里的Customer,就是一个User的配置文件,store–models.py:
from django.conf import settings
…
class Customer(models.Model):
…
user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}’
class Meta:
ordering = [‘user__first_name’, ‘user__last_name’]
添加了user,与User是一对一关系OneToOneField,我们这里不直接使用User,而是由 settings.AUTH_USER_MODEL指定以解耦,由于User类已经有first_name,last_name,email,所以删除Customer的这些字段,同时也修改__str__ 和 ordering属性,用来访问user的first_name,last_name等。
再进行迁移, 由于customer表增加了一个关联字段,已存的记录需要提供默认值设置具体的值, 我们设置为1。刷新就发现store_customer表已增加了一个字段user_id, 删除了first_name,last_name,email等字段了。
再修改store–admin.py的CustomerAdmin类:
class CustomerAdmin(admin.ModelAdmin):
list_display = [‘first_name’, ‘last_name’, ‘membership’, ‘orders’]
list_editable = [‘membership’]
list_per_page = 10
list_select_related = [‘user’]
ordering = [‘user__first_name’, ‘user__last_name’]
search_fields = [‘first_name__istartswith’, ‘last_name__istartswith’]
。。。
ordering改成 user__前缀的first_name等,添加list_select_related属性预加载user对象,另外 list_display不支持使用user__开头的前缀,所以我们需要在Customer模型类里添加first_name和last_name方法。
store–model.py:
class Customer(models.Model):
。。。
@admin.display(ordering=’user__first_name’)
def first_name(self):
return self.user.first_name
@admin.display(ordering=’user__last_name’)
def last_name(self):
return self.user.last_name
。。。
保存刷新网页,然后添加 customer测试正常。注意前面的@admin.display是为了能点标题排序。
组和权限
组(groups)是一组权限(permissions)的集合。我们创建一个组,名为Customer Service。并将Customer和Orders模型的权限赋予它。
这些权限在迁移模型时Django会自动帮我们生成,存储在auth_permission表中。
然后,再将该组赋给用户kelemi.
注意选中 Staff Status,表示允许用户kelemi登录管理台。
再注销并以kelemi登录,就会看到他允许管理 Customers和Orders了。
创建自定义权限
有时默认的权限不符合需求,可以创建自定义权限,比如取消订单就属于这类需求。
创建自定义权限非常简单,只需修改模型。
class Order(models.Model):
…
class Meta:
permissions = [
(‘cancel_order’, ‘can cancel order’)
]
permissions是一个列表,每个列表项是一个元组,元组第一项是代码引用的字符串,第二项是描述。然后保存并迁移到数据库,我们能在数据库auth_permission表中看到该自定义权限。
再按上述步骤,将该权限赋予用户kelemi。至于如何控制该权限的,请看下一篇:安全API。
小结
本文介绍了Django的用户认证系统。介绍认证系统是如何工作的,以及用户模型以及如何自定义:通过扩展用户模型或创建配置文件,并介绍如何通过权限和用户组来控制用户访问。
下一篇介绍安全API。