这个文档提供了如何定制权限系统的细节。
“认证”后端 在以下情形时可被扩展:当一个 User 模型对象带有用户名和密码时,且需要有别于 Django 默认的认证功能。
定制的权限。
实现 一个完全定制的模型。
有时候你需要挂接到其他认证资源 -- 另一包含用户名,密码的数据源或者其他认证方法。
对于一个在LDAP和Django网站都拥有账号的用户来说,如果他/她不能使用LDAP账号登录Django网站,对他/她以及网站管理员来说都是一件麻烦事。
您可以覆盖Django的基于数据库的默认方案,也可以连接使用其他系统的认证服务。
authentication backend reference。
如果第一个认证方法失败,Django 将尝试第二个,以此类推,直至试完所有的认证后台。
这些类可以位于Python 路径上任何地方。
AUTHENTICATION_BACKENDS 设置为:
('django.contrib.auth.backends.ModelBackend',)
你可以在自定义的认证后端中实现自己的速率控制机制,或者使用大部分Web 服务器提供的机制。
AUTHENTICATION_BACKENDS 的顺序很重要,所以如果用户名和密码在多个后台中都是合法的,Django 将在第一个匹配成功后停止处理。
Django 不会检查后面的认证后台。
注
Session.objects.all().delete().
认证方法。
User 对象.
大多数情况下,代码如下︰
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check the username/password and return a User.
...
当然,它也可以接收token的方式作为参数,例如:
class MyBackend(object):
def authenticate(self, token=None):
# Check the token and return a User.
...
None.
authenticate 方法在用户登陆的时候完成这件事。
User 对象:
from django.conf import settings
from django.contrib.auth.models import User, check_password
class SettingsBackend(object):
"""
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 = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
"""
def authenticate(self, 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. Note that we can set password
# to anything, because it won't be checked; the password
# from settings.py will.
user = User(username=username, password='get from settings.py')
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
自定义验证后端能提供自己的权限。
has_module_perms()) 那么user model就会给它授予相对应的许可。
也就是说,只要任意一个backend授予了一个user权限,django就给这个user这个权限。
PermissionDenied异常,授权将立即失败,Django不会检查接下来的后端认证。
上述的简单backend可以相当容易的完成授予admin权限。
class SettingsBackend(object):
...
def has_perm(self, user_obj, perm, obj=None):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
django.contrib.auth.models.User 同名函数将接收同样的参数,认证后台接收到的 user_obj,有可能是匿名用户 anonymous
ModelBackend并自定义后台API
在最基本的层面上,大多数网站授权匿名用户浏览大部分网站,许多网站允许匿名张贴评论等。
这对可重用应用的作者是很有用的, 因为他可以委托所有的请求, 例如控制匿名用户访问,给这个认证后端, 而不需要设置它
例如他们可以被允许激活他们的帐户。
对权限系统中的匿名用户的支持允许匿名用户具有执行某些操作的权限的情况,而未被认证的用户不具有。
is_active属性。
user_obj 给每一个对象相关的认证方法, 并且能够返回适当的对象级别的权限.
模型元属性。
此示例任务模型创建三个自定义权限,即用户是否可以对您的应用程序任务实例执行操作:
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"),
)
继续上面的示例,以下检查用户是否可以查看任务:
user.has_perm('app.view_task')
代理模型提供的功能包括默认的排序、自定义管理器以及自定义模型方法。
例如,你可以创建一个员工模型 (Employee model):
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
假设一个员工Fred Smith 既有User 模型又有Employee 模型,你可以使用Django 标准的关联模型访问相关联的信息:
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
User类注册的:
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)
django.db.models.signals.post_save 可以在适当的时候用于创建或更新相关模型。
但是,在你项目应用程序中,指向默认用户模型的链接可能带来额外的数据库负载。
例如,在某些网站上使用邮件地址而不是用户名作为身份的标识可能更合理。
AUTH_USER_MODEL 设置覆盖默认的User 模型, 其值引用一个自定义的模型。
AUTH_USER_MODEL = 'myapp.MyUser'
INSTALLED_APPS 中) 和你想使用的User 模型的名称。
注意
migrate 前设置它。
makemigrations支持的,并且会导致你需要手动修改数据库结构,从旧用户表中导出数据,可能重新应用一些迁移。
警告
否则, 你会碰到错误.
makemigrations
AUTH_USER_MODEL
AUTH_USER_MODEL,如下所述。
User(例如,通过一个外键引用它),你的代码将不能工作。
- get_user_model()[source]¶
-
User。
例如:
from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL)
New in Django 1.7:例如:
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)
get_user_model() 只在Django 已经导入所有的模型后才工作。
模型设计考虑
处理不直接相关的认证在自定义用户模型信息之前,应仔细考虑。
另一方面,查询来检索此相关的信息将涉及的数据库连接,这可能对性能有影响。
Django 期望你自定义的 User model 满足一些最低要求
- 可以是一个用户名,电子邮件地址,或任何其它独特属性。
- 然而,对这两种方式没有特定的要求,如果你想,他们可以返回完全相同的值。
Django的旧版本要求你的模型有一个整数主键也是如此。
然后,您必须提供一些关键的实施细节:
-
class models.CustomUser
-
-
USERNAME_FIELD
-
unique=True)。
identifier用作标识字段:
class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier'
New in Django 1.8.primary_key )的现有实例。
-
REQUIRED_FIELDS
-
REQUIRED_FIELDS在Django的其他部分没有任何影响,例如在管理员中创建用户。
User模型的部分定义:
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
注意
password,因为将始终提示输入这些字段。
New in Django 1.8.primary_key )的现有实例。
- is_active¶
-
model。
-
USERNAME_FIELD
AbstractBaseUser的任何子类:
BaseUserManager并提供两个额外的方法:
-
class models.CustomUserManager
BaseUserManager提供以下实用程序方法:
-
class models.BaseUserManager
-
- make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')¶
-
allowed_chars不包含可能导致用户混淆的字母,包括:
- 1(小写字母i,小写字母L,大写字母i,第一号)
- 0(小写字母o,大写字母o和零)
views对他们正在使用的用户模型做出某些假设。
如果您的用户模型不遵循相同的假设,可能需要定义替换表单,并作为auth视图配置的一部分传递该表单。
-
UserCreationForm
必须为任何自定义用户模型重写。
-
UserChangeForm
必须为任何自定义用户模型重写。
-
AuthenticationForm
USERNAME_FIELD中定义的字段。
-
PasswordResetForm
is_active的布尔字段,以防止对非活动用户进行密码重置。
-
SetPasswordForm
AbstractBaseUser的任何子类
-
PasswordChangeForm
AbstractBaseUser的任何子类
-
AdminPasswordChangeForm
AbstractBaseUser的任何子类
django.contrib.admin
这些方法允许管理员去控制User到管理内容的访问:
- class models.CustomUser
- has_perm(perm, obj=None):
-
obj,则需要针对特定对象实例检查权限。
- has_module_perms(app_label):
-
True。
django.contrib.auth.models.AbstractUser上不在您的自定义User类上的字段的任何定义。
这是一个抽象模型,您可以包含在用户模型的类层次结构中,为您提供支持Django权限模型所需的所有方法和数据库字段。
PermissionsMixin提供了以下方法和属性:
ModelBackend
如果您的用户模型未提供这些字段,则在检查权限时将收到数据库错误。
¶
通过定义自定义User模型,您可以删除Django可靠地识别基类的能力。
如果项目使用代理模型,则必须修改代理以扩展项目中当前使用的用户模型,或将代理的行为合并到用户子类中。
这包括使用夹具创建User实例的任何尝试。
这个装饰器可以应用于单个测试或整个测试类。
为了帮助这个,Django提供了两个可以在测试套件中使用的替代用户模型:
auth app提供的两个用户模型:
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.auth.tests.custom_user import CustomUser, ExtensionUser
from django.test import TestCase, override_settings
class ApplicationTestCase(TestCase):
@skipIfCustomUser
def test_normal_user(self):
"Run tests for the normal user model"
self.assertSomething()
@override_settings(AUTH_USER_MODEL='auth.CustomUser')
def test_custom_user(self):
"Run tests for a custom user model with email-based authentication"
self.assertSomething()
@override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
def test_extension_user(self):
"Run tests for a simple extension of the built-in User."
self.assertSomething()
此示例说明大多数组件如何协同工作,但不打算直接复制到项目以供生产使用。
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 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
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
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)
AUTH_USER_MODEL设置将自定义模型指定为项目的默认用户模型:
AUTH_USER_MODEL = 'customauth.MyUser'