2024年3月28日发(作者:)

/?2

Django中的用户认证

Django自带了一个用户授权认证系统。它可以处理用户帐户、组、权限和基于cookie

的用户会话。本篇文档将解释它是如何工作的。

目录

*1

*2

*3

概览

安装

用户(Users)

o3.1API参考

+3.1.1

+3.1.2

+3.1.3

o3.2

字段

方法

管理功能

基本用法

+3.2.1创建用户

+3.2.2更改密码

密码

匿名用户

创建超级用户

o3.3

o3.4

o3.5

*4Web请求中的认证

o4.1如何登录一个用户

o4.2如何登出用户

o4.3限制已登录用户的访问

+4.3.1原始的方法

+4.3.2login_required修饰符

*5

o4.4已登录用户通过通行测试(passtest)来限制访问

o4.5限制访问genericviews

权限(Permissions)

o5.1

o5.2

o5.3

默认权限

自定义权限

API参考

*6

+5.3.1字段

+5.3.2方法

模板中的认证数据

o6.1用户(Users)

o6.2权限(Permissions)

组(Groups)

消息(Messages)

其他认证资源

o9.1指定认证后端

o9.2编写一个认证后端

*7

*8

*9

/?2

1

概览

*用户(Users)

*权限(Permissions):二进制(yes/no)的标志,用来指明用户都能做哪些事情。

*组(Groups):向多个用户应用标签和权限的通用方法。

*消息(Messages):为给定的用户排队消息的一个简单的方法。

认证系统包括:

2

安装

1.把''放到你的INSTALLED_APPS设置中。

2.运行命令db。

认证支持作为Django的一个应用被绑定在中。安装方法如下:

注意,默认情况下,通过使用tproject来创建的工程已经在

中的INSTALLED_APPS包含了''。如果你的INSTALLED_APPS中已

经包含了'',你也可以再次运行db。你可以随意运行多

少次都无所谓,每一次它都仅仅安装需要的部分。

syncdb创建必要的数据表,同时也为已经安装的apps创建他们需要用到的权限对象。当你

第一次运行这个命令的时候,它还会提示你创建一个超级用户的帐户。

当你做完以上这些步骤之后,认证系统就安装好了。

3

3.1

3.1.1

用户

(Users)

API参考

字段

用户(Users)表现为一个标准的Django模型,他在django/contrib/auth/中。

User对象包含如下字段:

*username--必须。小于等于30个字符。(字母、数字和下划线)

*first_name--可选。小于等于30个字符。

*last_name--可选。小于等于30个字符。

*email--可选。电子邮件地址。Optional.E-mailaddress.

*password--必须。密码的hash值。(Django不保存原始密码。)原始密码可以是

/?2

任意长的并且可以包含任意字符。请看下面的“密码”节。

*is_staff--布尔型。标识用户能否访问admin界面。

*is_active--布尔型。标识用户能否登录到admin界面。如果不想删除用户请把它

设为False

*is_superuser--布尔型。标识用户可以得到所有的权限。

*last_login--用户上一次登录的日期时间。默认设置为当前的日期和时间。

*date_joined--用户帐户创建的日期。默认设置为帐户创建时的日期和时间。

3.1.2方法

User对象有2个多对多(many-to-many)的字段:groups和user_permissions。User对象

可以像其他Django对象(Djangomodel)那样访问他们关联的对象。

=[group_list]

(group,group,...)

(group,group,...)

()

sions=[permission_list]

(permission,permission,...)

(permission,permission,...]

()

除了这些自动生成的API方法外,User对象还有如下的自定义的方法:

*is_anonymous()--总是返回False。这是区别UserandAnonymousUser对象的

一个方法。通常你应该使用is_authenticated()而不是这个方法。

*is_authenticated()--总是返回True。这是测试用户是否被认证了。

*get_full_name()--返回first_name加last_name,中间用空格隔开。

*set_password(raw_password)--用给定的字符串设定用户密码,并且处理密码的

hash值。不保存User对象。

*check_password(raw_password)--如果给定的用户密码是正确的,那么返回

True。(通过比较密码的hash值来实现的。)

*get_group_permissions()--返回从用户所在的组里面获取的权限列表。

*get_all_permissions()--返回用户拥有的所有的权限。包括组权限和用户权限。

*has_perm(perm)--当参数的格式为"me"的时候,并且用户拥有

特殊权限的时候,返回True。

*has_perms(perm_list)--同上。用户对列表中每一个参数都有特殊权限的时候。每

一个参数的格式都是"me"。

*has_module_perms(package_name)--当用户对给定的包(Djangoapplabel)有权限

的时候返回True。

*get_and_delete_messages()--返回用户对列中的Message对象列表并且从对列

中删除Message对象。

*email_user(subject,message,from_email=None)--向用户发送e-mail。如果

/?2

from_email是None,Django使用DEFAULT_FROM_EMAIL设置。

*get_profile()--返回站点特定的用户档案。如果当前站点不允许查询档案的话,

Django将抛出ofileNotAvailable异常。

3.1.3管理功能

User模型有一个自定义的管理器,它有如下的函数:

*

create_user(username,email,password)--创建,保存并返回一个User。

username,email和password被设置为给定的值,并且User设置了is_active=True。

请看下面的创建用户中的例子。

*

make_random_password(length=10,

allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')

返回一个给定长度的并且是包含给定字符的随机密码。(注意:allowed_chars的默认值不包

括"I"或者类似的字符,这是为了避免字符分辨不清而产生的抱怨。)

3.2

3.2.1

基本用法

创建用户

创建用户最基本的方法就是使用Django提供的create_user函数:

>>>importUser

>>>user=_user('john','lennon@','johnpassword')

#在这里,User对象已经可以保存到数据库中了。

#你还可以改变它的其它属性。

>>>_staff=True

>>>()

3.2.2更改密码

使用set_password()更改密码:

>>>importUser

>>>u=(username__exact='john')

/?2

>>>_password('newpassword')

>>>()

除非你很清楚的知道你在做什么,否则不要直接设置password属性。这将在下一节中阐释。

3.3密码

User的password属性是一个如下格式的字符串:

hashtype$salt$hash

它们是hashtype,salt和hash,用一个美元符号来分隔的。

Hashtype是sha1(默认)或md5--单向加密的方法。Salt是一个随机字符串用来为密码的

原文创建hash。

例如:

sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4

_password()和_password()函数负责处理设置和检测这些密码。

在Django先前的版本中,例如0.90,仅使用MD5而不使用salt。为了向后兼容,这些依然

被支持。他们会在第一次使用check_password()来成功检测用户密码的时候自动将其转换

为新的格式。

3.4匿名用户

类,它实现了ousUser是一个

接口,有如下的不同点:

*id总是None.

*is_anonymous()返回True而不是False.

*is_authenticated()返回False而不是True.

*has_perm()总是返回False.

*set_password(),check_password(),save(),delete(),set_groups()andset_permissions()

抛出NotImplementedError异常.

在实际应用中,你可能并不需要AnonymousUser对象。但是他们被应用到网络请求(Web

request)中,这将在下面的章节中讲述。

3.5创建超级用户

/?2

在你将''添加到INSTALLED_APPS中之后,并且第一次运行

db命令时,它将提示你创建一个超级用户。但是如果你需要在之后的操作过

程中用命令行创建超级用户的话,你可以使用create_。命令如下:

python/path/to/django/contrib/auth/create_

请确认你已经将/path/to/加入到PYTHON_PATH中。

4Web

请求中的认证

到目前为止,本文档已经介绍了操作认证相关的对象的低级的API。在高一级上,Django

可以将认证框架挂接到它本身系统的请求对象(requestobjects)中。

首先,安装SessionMiddleware和AuthenticationMiddleware中间件。把他们加入到

MIDDLEWARE_CLASSES设置中即可。更多信息请看sessiondocumentation。

当你安装好这些中间件之后,你就可以在视图(view)中访问了。将

返回当前登录的用户的一个User对象。如果当前没有用户登录,那么将返回

一个AnonymousUser对象的实例。你可以通过is_authenticated()来判断是否有用户登录,

如下:

_authenticated():

#认证的用户

else:

#匿名用户

4.1如何登录一个用户

Django在提供了2个函数:authenticate()和login()。

如果通过给定的用户名和密码做认证,请使用authenticate()函数。他接收2个参数,一个

是username一个是password。如果认证成功,它返回一个User对象。如果密码无效,

它返回一个None。例如:

portauthenticate

user=authenticate(username='john',password='secret')

ifuserisnotNone:

print"用户名、密码正确!"

else:

print"用户名、密码错误!"

在视图中登录一个用户的话,使用login()函数。它接收HttpRequest对象和一个User对

象。login()通过Django的session框架把用户的ID保存到session中。所以,你要确认你

/?2

已经安装了session中间件。

下面是合用authenticate()和login()的例子:

portauthenticate,login

defmy_view(request):

username=['username']

password=['password']

user=authenticate(username=username,password=password)

ifuserisnotNone:

login(request,user)

#转到成功页面

else:

#返回错误信息

4.2如何登出用户

()登录的用户的话,可以在视图中使用要登出使用

()。它接收一个HttpRequest参数,没有返回值。例如:

portlogout

deflogout_view(request):

logout(request)

#转到成功页面

请注意:如果用户没有登录的话,logout()也不会抛出任何异常的。

4.3

4.3.1

限制已登录用户的访问

原始的方法

最简单、最原始的限制页面访问的方法是在每个页面上加入_authenticated()

并且把它重定向到登录页面。

portHttpResponseRedirect

defmy_view(request):

_authenticated():

returnHttpResponseRedirect('/login/?next=%s'%)

#...

/?2

或者显示一条出错信息

defmy_view(request):

_authenticated():

returnrender_to_response('myapp/login_')

#...

4.3.2login_required

修饰符

你可以使用login_required修饰符来作为一个快捷方式

torsimportlogin_required

defmy_view(request):

#...

my_view=login_required(my_view)

下面是一个等价的例子,使用了Python2.4的decorator样式

torsimportlogin_required

@login_required

defmy_view(request):

#...

login_required作下面这些事情:

*如果用户没有登录,那么重定向到/accounts/login/,传入当前的绝对URL路径

作为querystringnext的值。例如:/accounts/login/?next=/polls/3/。

*如果用户已经登录了,那么就正常执行view的代码。

请注意,你需要映射正确的处理登录用的视图(view)到/accounts/login/。把下面的行加入到

你的URLconf中:

(r'^accounts/login/$',''),

的作用是:

*如果通过``GET``方式调用的话,它显示一个登录表单并通过POST的方式登录。

*如果通过``POST``方式调用的话,它试图把用户登录进去。如果登录成功,

视图(view)重定向到``/accounts/profile/``(目前是硬性编码的,就是写死的。)。

如果登录失败,则继续显示登录表单。

/?2

你需要自己提供一个登录表单的模版,默认叫registration/。这个模版需要获得3

个模版上下文的变量:

*form:一个FormWrapper对象,用来显示登录表单。更多请看``FormWrapper``

对象的formsdocumentation。

*next:登录成功后重定向的URL。也可能包含一个查询字符串。

*site_name:当前Site的名字。根据SITE_ID设置的信息获取。参考site

frameworkdocs。

如果你不想使用registration/这个模版,你可以为在URLconf中的视图(view)传入

一个template_name作为扩展的参数。

(r'^accounts/login/$','',{'template_name':'myapp/'}),

下面是一个registration/的例子,你可以以它为基础来开始你的工作。它扩展自

并且定义了一个content块:

{%extends""%}

{%blockcontent%}

{%_errors%}

用户名和密码不匹配。请重试。

{%endif%}

用户名:{{me}}
密码:{{rd}}

{%endblock%}

4.4已登录用户通过通行测试(passtest)来限制访问

为了实现基于特定的权限或其他的测试的访问限制,你应该做同上一节一样的基本事情。To

limitaccessbasedoncertainpermissionsorsomeothertest,you'ddoessentiallythesamethingas

describedintheprevioussection.

/?2

简单的方法是在view中直接运行测试的代码。例如,这个view检测并确认用

户已经登陆并且拥有_vote的权限。

defmy_view(request):

ifnot(_authenticated()_perm('_vote')):

returnHttpResponse("你不能投票。")

#...

你可以使用user_passes_test作为快捷方式

torsimportuser_passes_test

defmy_view(request):

#...

my_view=user_passes_test(lambdau:_perm('_vote'))(my_view)

Python2.4的写法

torsimportuser_passes_test

@user_passes_test(lambdau:_perm('_vote'))

defmy_view(request):

#...

user_passes_test()需要一个必须的参数:一个包含User的可调用的对象,如果用户被允许

察看这个页面的话,返回True。注意,如果User不是匿名的话,user_passes_test并不

自动监测。

user_passes_test()需要一个可选的login_url参数,它可以让你指定登录表单的URL(默认

是/accounts/login/)。

例子,Python2.3写法

torsimportuser_passes_test

defmy_view(request):

#...

my_view=user_passes_test(lambdau:_perm('_vote'),login_url='/login/')(my_view)

例子,Python2.4写法

torsimportuser_passes_test

/?2

@user_passes_test(lambdau:_perm('_vote'),login_url='/login/')

defmy_view(request):

#...

4.5限制访问genericviews

为了限制访问genericview,可以为view写一个包装器,并且在URLconf中指定为它。

例如

_basedimportobject_detail

@login_required

deflimited_object_detail(*args,**kwargs):

returnobject_detail(*args,**kwargs)

5

权限

(Permissions)

Django自带了一个简单的权限系统。它为向用户和用户组付权限提供了一个途径。

它被用在了Django的admin站点中,当然你也可以把它用在自己的代码中。

Django的admin站点是这样应用权限的:

*通过"add"权限来控制用户是否可以访问添加表单并添加一个指定类型的对象。

*通过"change"权限来控制用户是否可以访问指定类型对象的列表和修改表单。

*通过"delete"权限来控制用户是否可以删除指定类型的对象。

权限被赋予每种类型的对象,而不是对象的特定的实例。你可以说“玛丽可以修改新的故事

(stories)”,但是你不能说“玛丽可以修改她创建的新的故事”或者“玛丽只能修改特定状态

的、特定发布时间的、特定ID的故事等等”。这些功能目前Django的开发人员还在讨论之

中。

5.1默认权限

3个基本的权限--添加(add),创建(create)和删除(delete)--在创建包含有classAdmin的

Django模型的时候都自动被创建好了。在表面现象的后面,当你运行db的

时候,这些权限被添加到了auth_permission数据表中。

请注意,如果你的模型里没有classAdmin的话,当你运行db的时候这些

权限不会被创建出来。如果你初始化数据库之后还想添加这些权限,可以在模型中加入class

/?2

Admin然后再运行一次db。

5.2自定义权限

为了给指定的模型自定义权限,可以使用权限(permissions)的modelMetaattribute。

这个例子创建了3个自定义的权限

classUSCitizen():

#...

classMeta:

permissions=(

("can_drive","Candrive"),

("can_vote","Canvoteinelections"),

("can_drink","Candrinkalcohol"),

)

接下来的事情就是运行syncdb来创建这些权限。

5.3API参考

就像用户对象,权限也是实现自Django的模型django/contrib/auth/.

5.3.1字段

Permission有如下这些字段:

*name--必须。小于等于50个字符。例如:'Canvote'。

*content_type--必须。引用自django_content_type数据表,它包含了已经安装的

Django模型的类型。

*codename--必须。小于等于100个字符。例如:'can_vote'。

5.3.2方法

Permission有着与其它Djangomodel一样的数据访问方法。

6模板中的认证数据

如果你使用RequestContext的话,已经登录的用户的user和权限对象就保存在template

context。

技术细节

/?2

在技术上,只有当你使用RequestContext的时候并且你的

TEMPLATE_CONTEXT_PROCESSORS设置包含"t_"的

时候,这个变量才是有效的。更多参见RequestContextdocs.

6.1用户(Users)

当前登录的用户,不管是否是匿名的,存储在模版变量{{user}}中。

{%_authenticated%}

欢迎,{{me}}。谢谢您的来访。

{%else%}

欢迎,请登录。

{%endif%}

6.2权限(Permissions)

{{perms}}中。他是当前登录用户的权限存储在模版变量

t_apper的实例。

在{{perms}}对象中,单个属性的查找是使用_module_perms的。下面这个例子

中,如果用户对foo这个app有任何权限的话,它就返回True。

{{}}

二级属性查找是使用_perm。下面这个例子中,如果用户有_vote权限的

话,它就返回True。

{{_vote}}

因此,你可以在模板中用{%if%}语句来判断权限

{%%}

你有操作foo的权限。

{%_vote%}

你可以投票。

{%endif%}

{%_drive%}

你可以开车。

{%endif%}

{%else%}

你没有操作foo的权限。

{%endif%}

/?2

7组(Groups)

组通常用来归类用户,这样你就可以为这些组里面的用户应用权限或者贴其他的标签。一个

用户可以属于任意数量的组。

组中的用户自动获得赋予组的权限。例如,如果组Siteeditors有can_edit_home_page的权

限,那么任何加入这个组的用户都自动拥有这个权限。

组也是归类用户并给他们贴标签或扩展功能的一个方便的途径。例如,你创建一个'Special

users'的组,你可以写代码来让他们访问网站的会员专区或者发送给他们会员专用的电子邮

件。

8

消息

(Messages)

消息系统是为给定用户排队消息的轻量级的途径。

一个消息关联到一个User对象。没有过期标志和时间戳。

Django的admin站点用它来发送成功操作后的消息。例如,"poll成功被创建。"就是一条

消息。

API很简单

*创建消息用

``user_e_(message='message_text')``。

*检索/删除消息用``user__and_delete_messages()``,

他返回用户消息队列(如果有的话)中的一个``Message``列表,并且从队列中删除这些消

息。

下面的例子中,用户创建一个播放列表之后系统把消息保存下来

defcreate_playlist(request,songs):

#用给定的歌曲创建一个播放列表

#...

e_(message="播放列表添加成功。")

returnrender_to_response("playlists/",

context_instance=RequestContext(request))

如果你使用RequestContext,当前用户和他的消息保存在templatecontext的模版变量

{{messages}}里。下面是在模板中显示消息的例子

{%ifmessages%}

    /?2

    {%formessageinmessages%}

  • {{e}}
  • {%endfor%}

{%endif%}

注意,RequestContext调用get_and_delete_messages。所以,就算你不显示他们的话,他

们也会被删除的。

最后,请注意这个消息框架仅适用于在用户数据库中的用户。如果要向匿名用户发送消息的

话,请使用sessionframework。

9

其他认证资源

Django自带的认证系统对于大多数情况来说已经足够用了,但是你也可能需要使用其他的

认证源。其他的用户名和密码或者认证方法等。

例如,你的公司可能已经有了LDAP,并且已经存储了每一位雇员的用户名和密码。如果你

让网络管理员和用户在LDAP和基于Django的应用上使用不同的登录帐户的话,他们会非

常不高兴的。

所以,为了解决这个问题,Django的认证系统允许你插入其他的认证源。你可以重载Django

默认的数据库结构,或者你可以把默认的系统和其他的系统串联起来。

9.1指定认证后端

在暗中,Django维护一个"authenticationbackends"的列表用来测试认证。当某人调用

ticate()--上面提到的"如何登录一个用户"--Django将尝试所有的

认证后端。如果第一个认证方法失败了,Django将会继续尝试第二个,直到所有的都被尝

试过。

认证后端的列表在AUTHENTICATION_BACKENDS设置。内容应该是包含Python路径的

元组。

默认情况下,AUTHENTICATION_BACKENDS设置为

('ackend',)

这是检测Django用户数据库的基本认证方案。

按照AUTHENTICATION_BACKENDS的排列顺序,如果同样的用户名和密码在第一次就

匹配了,那么Django将停止处理后面的东西。

9.2编写一个认证后端

/?2

一个认证后端是一个类,实现了2个方法:get_user(id)和authenticate(**credentials)。

get_user方法接受一个id--可以是用户名,数据库ID或者其他的什么--并且返回一个

User对象。

authenticate方法接受字典型认证信息的参数。大多情况下是如下样子的

classMyBackend:

defauthenticate(username=None,password=None):

#检测用户名和密码,并返回一个User。

他也可以处理一个代号(token),像这样

classMyBackend:

defauthenticate(token=None):

#检测并返回User。

当authenticate接受的参数被验证为有效的时候,应该返回一个User对象;如果无效的时

候,应该返回None。

在本文当开头提到,Django的admin系统紧密地与User对象绑定在一起。目前,最好的

处理方法就是为你每一个现存的后端(例如,你的LDAP目录或者你的外部SQL数据库等等。)

数据创建一个Django的User对象。你可以预先写一个脚本来做这些事情,或者在用户第

一次登录的时候在你的authenticate方法中做这些事情。

下面是一个例子,使用在文件里定义的用户名和密码并且在用户第一次登录的

时候创建一个Django的User对象。

portsettings

importUser,check_password

classSettingsBackend:

"""

AuthenticateagainstthesettingsADMIN_LOGINandADMIN_PASSWORD.

Usetheloginname,mple:

ADMIN_LOGIN='admin'

ADMIN_PASSWORD='sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'

"""

defauthenticate(self,username=None,password=None):

login_valid=(_LOGIN==username)

pwd_valid=check_password(password,_PASSWORD)

/?2

iflogin_validandpwd_valid:

try:

user=(username=username)

tExist:

#创建新用户。

#我们可以设置任何新的密码,因为它不会被检测。

#在这里我们使用""。

user=User(username=username,password='')

_staff=True

_superuser=True

()

returnuser

returnNone

defget_user(self,user_id):

try:

(pk=user_id)

tExist:

returnNone