Customizing authentication in Django
Django提供的认证(authentication)是不够好,最常见的情况是它已经不能满足需要的默认框。要自定义验证您的项目需要涉及到理解什么点所提供的系统的可扩展或更换。本文档提供了如何认证系统可定制的细节。
当用户名和密码存需要针对不同的储用户模型服务,而不是Django默认的认证。后端认证(Authentication backends)提供了一个可扩展的系统
你可以给你的模型自定义能通过Django检查权限的授权系统。
您可以选择扩展默认的用户模型,或替换一个完全定制的模式。
其他验证来源
有时可能你有需要挂接到另一个验证源 - 也就是说,用户名和密码或身份验证方法的另一个来源。
例如,您的公司可能已经为每一位员工做了一个LDAP设置存储用户名和密码。如果用户有独立账户,基于Django的应用的LDAP会是一个麻烦的网络管理员和用户本身。
因此,要处理这样的情况下,Django的认证系统,允许你插上其他来源的认证。您可以覆盖Django默认的数据库为基础的方案,或者您可以使用默认的系统与其他系统同步。
认证后端后端身份验证与Django信息参考。
指定认证后端(Specifying authentication backends)
在幕后,Django的维护“认证后端”检查认证的列表。如果有人访问网站 django.contrib.auth.authenticate() 用来描述如何记录用户在上,关于Django的尝试验证其在所有认证后端。如果第一种验证方法失败,Django的尝试第二个,等等,直到所有的后端已经尝试。
AUTHENTICATION_BACKENDS 这个应该是一个Python路径名称的元组,认识如何进行验证的的Python类。这些类可以在你的Python路径的任何地方。
By default, AUTHENTICATION_BACKENDS is set to:
('django.contrib.auth.backends.ModelBackend',)
这是检查Django的用户数据库和查询内置权限的基本认证后端
它不提供保护防止通过蛮力攻击的任何速率限制的机制。您可以自定义的验证后端机制 实现自己的速率限制,或使用大多数Web服务器提供的机制。
note
Once a user has authenticated, Django stores which backend was used to authenticate the user in the user’s session, and re-uses the same backend for the duration of that session whenever access to the currently authenticated user is needed. This effectively means that authentication sources are cached on a per-session basis, so if you change AUTHENTICATION_BACKENDS, you’ll need to clear out session data if you need to force users to re-authenticate using different methods. A simple way to do that is simply to execute Session.objects.all().delete().
写一个认证后端
必须实现两个方法,以及一组可选的许可相关的授权方法。
get_user(user_id)
authenticate(**credentials)
GET_USER方法需要user_id - 这可能是一个用户名,数据库ID或什么,但必须是用户对象的主键 - 返回一个User对象。
class MyBackend(object): def authenticate(self, username=None, password=None): # Check the username/password and return a User. ...
class MyBackend(object): def authenticate(self, token=None): # Check the token and return a User. ...
无论哪种方式,验证应检查它得到凭据,而它应当返回一个User对象符合这些凭据,凭据是否有效。如果他们是无效的,它应该返回无。
Django管理系统是紧密结合在本文开头描述的的Django用户的对象。就目前而言,最好的方式来处理这个是您的后端(例如,在您的LDAP目录,外部SQL数据库等),您可以编写一个脚本来做到这一点的存在为每个用户创建一个Django的User对象提前或您的验证方法可以做它的第一次用户登录
下面是一个后端例子,并创建一个的Django用户的对象的用户进行验证 在settings.py文件中定义的变量第一次进行身份验证用户名和密码:
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
定制的后端处理授权
自定义身份验证后端可以提供自己的权限。
用户模型下放权限查询函数
(get_group_permissions(), get_all_permissions(), has_perm(), has_module_perms())来实现这些功能的所有后端认证
赋予用户所有的后端返回的所有权限扩展集 权限。也就是说,Django的授予用户的某个权限,所有一个后端补助。
class SettingsBackend(object): ... def has_perm(self, user_obj, perm, obj=None): if user_obj.username == settings.ADMIN_LOGIN: return True else: return False
自定义权限
要创建一个给定的模型对象的自定义权限,使用权限模型的Meta属性。
这个例子中的任务模型创建三个自定义许可权,即用户可以或不可以做任务实例,具体到您的应用程序:
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 syncdb的创建这些额外的权限。你的代码是负责检查这些权限的值,当用户试图访问应用程序所提供的功能(查看任务,改变任务的状态,关闭任务。)继续上面的例子,以下检查,如果用户可以查看任务:
user.has_perm('app.view_task')
扩展现有的用户模型
有两种方法来扩展默认的用户模型,而不能用自己的模型取代。如果你需要的是纯粹的行为,不需要任何改变什么是存储在数据库中,你可以创建一个基于用户的代理模型。这允许所有代理模型,包括默认排序,自定义管理人员,或自定义模型方法提供的功能。
第二种方法,如果你想存储有关用户的信息,你可以使用一个一对一的关系到模型包含更多信息的字段。此一对单模型通常被称为剖面模型,因为它可能存储非认证相关的信息,一个用户。例如,您可以创建一个Employee模型:
from django.contrib.auth.models import User class Employee(models.Model): user = models.OneToOneField(User) department = models.CharField(max_length=100)
假设现有职工弗雷德·史密斯有一个用户和一个员工模式,您可以访问使用Django的标准相关示范公约的相关信息:
>>> u = User.objects.get(username='fsmith') >>> freds_department = u.employee.department
管理员给用户的页面中添加一个轮廓模型的字段,定义InlineModelAdmin(在这个例子中,我们将使用一个StackedInline)在您的应用程序admin.py,并把它添加到用户类注册一个useradmin的类:
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)
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
AUTH_USER_MODEL = 'myapp.MyUser'
你不应该 直接指向用户模型的方式引用的用户模型使用django.contrib.auth.get_user_model()。此方法将返回当前活动的用户模式 - 如果指定了一个自定义的用户模型,或以其他方式使用。
当你定义一个外键或许多到多用户模式的关系,你应该指定的定制模型使用AUTH_USER_MODEL设置。例如:
from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL)
指定一个自定义的用户模型
Django的期待您的自定义用户模式,以满足一些最低要求。
你的模型必须有一个整型主键。
您的模型必须有一个唯一的字段,可用于识别目的。这可以是一个用户名,电子邮件地址,或任何其他的独特属性。
您的模型必须提供一种方法,以解决用户在一个“短”和“长”的形式。最常见的解释是使用用户的名称为“短”的标识,用户的全名“长”标识符。然而,没有任何限制,这两种方法返回 - 如果你愿意,他们可以返回相同的值。
构建一个兼容的自定义用户模型的最简单的方法是继承从AbstractBaseUser。 AbstractBaseUser提供用户模型的核心实现,包括散列的密码和记号化的密码重设。然后,你必须提供一些关键的实现细节:
class models.CustomUser
USERNAME_FIELD
一个字符串的字段名称描述用户模型所使用的唯一标识符。这通常是某种形式的用户名,但它也可以是一个电子邮件地址,或者任何其他唯一标识符。字段必须是唯一的(即具有独特= true设置在其定义)。
class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True, db_index=True) ... USERNAME_FIELD = 'identifier'
REQUIRED_FIELDS
-
通过的createsuperuser管理命令创建用户时,系统会提示字段名列表。用户将被提示为每个字段提供一个值。它必须包括任何空白领域是虚假或不确定的,可能包括额外的字段,你想要当用户以交互方式创建提示。然而,它不会工作,为外键字段。 REQUIRED_FIELDS Django的其他部分没有任何影响,就像创建一个管理员用户。
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
is_active
一个布尔属性指示用户是否被认为是“积极的”。作为一个属性此属性默认为True AbstractBaseUser。你如何选择来实现,这将取决于您所选择的认证后端的细节。内置的用户模型细节,请参阅文档的属性。
get_full_name()
get_short_name()
一个简短的,非正式的为用户标识符。一个常见的解释将是第一个用户的名称,但它可以是任何字符串,用于标识用户在一种非正式的方式。它也可能返回相同的值
class models.AbstractBaseUser
get_username()
is_anonymous()
始终返回false。这是的方式分化从AnonymousUser对象。一般来说,你应该更喜欢使用is_authenticated()这个方法。
is_authenticated()
总是返回true 。这是一种方式来告诉,如果用户已经通过验证。这并不意味着任何权限,不检查,如果用户是活跃的 - 它只是表明用户提供了一个有效的用户名和密码。
set_password(raw_password)
给定的原始字符串,用户的密码设置的口令散列照顾。是否不保存AbstractBaseUser的对象。
check_password(raw_password)
返回True ,如果给定的字符串是正确的用户的密码。 (这需要照顾的口令散列作比较)
set_unusable_password()
标志着用户没有设置密码。这是不一样的,作为具有一个空字符串输入密码。 check_password()此用户将永远不会返回True。是否不保存AbstractBaseUser的对象。
您可能需要这一点,如果您的应用程序的身份验证发生对现有的外部源,如LDAP目录。
has_usable_password()
返回false如果set_unusable_password()被称为此用户。
class models.CustomUserManager
-
create_user(*username_field*, password=None, **other_fields)
create_user()的原型应该接受username字段,以及所有必需的字段作为参数。例如,如果您的用户模型使用电子邮件作为用户名“字段中,有DATE_OF_BIRTH作为必填字段,然后create_user应定义为:
def create_user(self, email, date_of_birth, password=None): # create user here ...
create_superuser(* username_field*,密码,** other_fields)
的原型create_superuser()应该接受username字段,以及所有需要的字段作为参数。例如,如果您的用户模型使用电子邮件作为用户名“字段中,有DATE_OF_BIRTH作为必填字段,然后create_superuser应定义为:def create_superuser(self, email, date_of_birth, password): # create superuser here ...
BaseUserManager提供以下实用方法:
-
class
models.
BaseUserManager
-
-
normalize_email(
email)
a类方法标准化,电子邮件地址小写的电子邮件地址的域部分。
-
get_by_natural_key(
username)
-
make_random_password(
length=10,
allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
- i, l, I, and 1 (lowercase letter i, lowercase letter L, uppercase letter i, and the number one)
- o, O, and 0 (uppercase letter o, lowercase letter o, and zero)
扩展Django默认的用户
如果你完全满意Django的用户模型,只是想和你添加一些额外的配置文件信息,你可以简单地的子类django.contrib.auth.models.AbstractUser和添加自定义的配置文件字段。这个类提供了默认的用户作为一个抽象的模型全面实施。
自定义用户和内置的验证表格
正如你可能想到,内置Django的表单和视图使用户模型的某些假设,他们正在与。
如果您的用户模型不遵循相同的假设,它可能有必要定义一个替换的形式,并在作为配置的auth视图的一部分,通过这种形式。UserCreationForm
取决于用户模型。必须重新写任何用户自定义模式。
UserChangeForm
取决于用户模型。必须重新写任何用户自定义模式。
AuthenticationForm
工程与的任何子类AbstractBaseUser,将适应使用该字段定义在USERNAME_FIELD。
PasswordResetForm
假设用户模型有一个整数的主键,有一个字段名为email,可以用来识别用户,一个布尔字段名为is_active防止无效用户密码重置的。
SetPasswordForm
工程与任何AbstractBaseUser子类
PasswordChangeForm
工程与任何AbstractBaseUser子类
AdminPasswordChangeForm
工程与任何AbstractBaseUser子类定制用户和django.contrib.admin
如果您希望自定义用户模型,还与帮助,您的用户模型必须定义一些附加的属性和方法。这些方法允许管理员控制用户访问管理内容:
类models.CustomUser
is_staff
返回true,如果用户被允许访问管理站点。
is_active
返回true,如果用户帐户是目前活跃。
has_perm (烫发, OBJ =无) :
返回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不在您的定义,是指自定义用户类。Custom users and permissions自定义用户和权限
可以很容易地到自己的用户类包括Django的许可框架,Django提供PermissionsMixin的。这是一个抽象的模型,可以包括在用户模式的类层次结构,给你所有的方法和必要支持Django的权限,模型数据库字段。
PermissionsMixin提供了以下的方法和属性:
类models.PermissionsMixin
is_superuser
布尔值。指定该用户拥有所有权限不明确分配。
get_group_permissions ( OBJ无)
该用户具有权限的字符串返回一组,通过他/她的团队。
如果obj是通过这个特定的对象,只返回该组的权限。
get_all_permissions ( OBJ无)
该用户具有的权限字符串返回一组,通过组和用户权限。
如果obj过去了,只返回这个特定对象的权限。
has_perm (烫发, OBJ =无)
如果用户指定的权限,烫发的格式“ <app标签> <permission codename> ”(见权限) ,则返回True 。如果用户是无效的,此方法将始终返回False。
如果obj传递,这种方法不会检查权限模型,但这个特定的对象。
has_perms ( perm_list , OBJ =无)
返回True如果用户指定的权限,每个烫发的格式为“ <app标签> 。 <permission codename> ” 。如果用户是无效的,此方法将始终返回False。
如果obj传递,这种方法不会检查模型的权限,但对特定对象进行。
has_module_perms ( PACKAGE_NAME)
返回True ,如果用户有权限在给定的包( Django应用程序标签) 。如果用户是无效的,此方法将始终返回False。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): """ 创建并保存一个用户给定的电子邮件,日期
出生和密码。 """ if not email: raise ValueError('Users must have an email address') user = self.model( email=MyUserManager.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): """ 创建并保存一个具有超级用户给定的电子邮件,日期
出生和密码。 """ 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, db_index=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): #用户确定他们的电子邮件地址 return self.email def get_short_name(self): #用户确定他们的电子邮件地址 return self.email def __unicode__(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): "“用户是否有一个具体的权限吗?”
#最简单的可能的答案是:是的,总是
return True @property def is_staff(self):“是用户工作人员的一员吗?”
#最简单的可能的答案:所有管理员的工作人员return self.is_admin
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 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',)}), ('Important dates', {'fields': ('last_login',)}), ) # 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 builtin permissions, # unregister the Group model from admin. admin.site.unregister(Group)
-