Customizing authentication in Django

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数据库等)可以编写一个脚本来做到这一点存在为每个用户创建一个DjangoUser对象提前验证方法可以做它的第一次用户登录

下面是一个后端例子并创建一个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()

一个简短的,非正式的为用户标识符一个常见解释将是第一个用户的名称但它可以是任何字符串,用于标识用户在一种非正式的方式也可能返回相同的值

django.contrib.auth.models.User.get_full_name().

class models.AbstractBaseUser   

   get_username()        

        返回的字段值由USERNAME_FIELD提名。

    
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()被称为此用户。
 
你也应该定义一个自定义用户模式经理如果用户模型定义的用户名电子邮件is_staffis_active is_superuser last_logindate_joined领域Django的默认用户一样可以安装Django的UserManager然而,如果用户模型定义不同的领域必须定义一个自定义管理器扩展BaseUserManager提供两个额外方法

 

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)
检索一个用户使用的字段内容USERNAME_FIELD获提名实例
make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
与给定长度和允许的字符给定的字符串返回一个随机密码 请注意默认值允许字符allowed_chars不包含字母,可引起用户的困惑,包括
      
  • 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)

 

posted @ 2013-10-05 14:08  lindi  阅读(1142)  评论(0编辑  收藏  举报