Django 中的用户认证¶
Django 自带一个用户认证系统,这个系统处理用户帐户、组、权限和基于 cookie 的会话。本文说明这个系统是如何工作的。
概览¶
认证系统由以下部分组成:
- 用户
- 权限:控制用户进否可以执行某项任务的二进制(是/否)标志。
- 组:一种为多个用户加上标签和权限的常用方式。
- 消息:一种为指定用户生成简单消息队列的方式。
安装¶
认证系统打包在 Django 的 django.contrib.auth 应用中,安装它有以下步骤:
- 把 'django.contrib.auth' 和'django.contrib.contenttypes' 放到INSTALLED_APPS 设置中。(django.contrib.auth 中的Permission 模型依赖于django.contrib.contenttypes 。)
- 运行命令 manage.py syncdb 。
注意:为了方便起见, django-admin.pystartproject 创建的缺省:file:settings.py 文件的INSTALLED_APPS 中已包含'django.contrib.auth' 和'django.contrib.contenttypes' 。如果你的INSTALLED_APPS 早已包含这些应用,你可以放心地再次运行manage.pysyncdb 。这个命令可以多次执行,因为每次它只会安装需要安装的东西。
syncdb 命令创建必须的数据库表和所有已安装应用需要的权限对象。第一次运行这个命令会提示你创建一个超级用户。
通过以上步骤,认证系统就安装完成了。
用户¶
-
class
models.
User
¶
API 手册¶
字段¶
-
class
models.
User
-
User 对象有以下字段:
-
username
¶
-
必选项。 小于等于 30 个字符。 只能是字母数字(字母、数字和下划线)。
Changed in Django 1.2: 用户名现在可以包含 @ 、 + 、 . 和 - 字符。
-
first_name
¶
-
可选项。 小于等于 30 个字符。
-
last_name
¶
-
可选项。 小于等于 30 个字符。
-
email
¶
-
可选项。电子邮件地址。
-
password
¶
-
必选项。密码(哈希值,元数据)。 Django 不储存原始密码。原始密码可以是任意长度的,包含任何字符。参见下面“密码”一节。
-
is_staff
¶
-
布尔值。指明这个用户是否可以进入管理站点。
-
is_active
¶
-
布尔值。指明这个用户帐户是否是活动的。我们建议把这个标记设置为 False 来代替删除用户帐户,这样就不会影响指向用户的外键。
这个属性不控制用户是否可以登录。登录验证时不会核查 is_active 标志。因此,如果在登录时需要检查is_active 标志,需要你在自己的登录视图中实现。但是用于login() 视图的AuthenticationForm会 执行这个检查,因此应当在 Django 站点中进行认证并执行 has_perm() 之类的权限检查方法。所有那些方法或函数对于不活动的用户都会返回False 。
-
is_superuser
¶
-
布尔值。指明用户拥有所有权限(包括显式赋予和非显式赋予的)。
-
last_login
¶
-
缺省情况下设置为用户最后一次登录的日期时间。
-
date_joined
¶
-
缺省情况下设置为用户帐户创建的日期时间。
-
方法¶
-
class
models.
User
-
User 对象有两个多对多字段 fields: models.User. 组( groups ) 和 用户权限(user_permissions ) 。 User 对象可以象其它Django 模型 一样操作关联对象:
myuser.groups = [group_list] myuser.groups.add(group, group, ...) myuser.groups.remove(group, group, ...) myuser.groups.clear() myuser.user_permissions = [permission_list] myuser.user_permissions.add(permission, permission, ...) myuser.user_permissions.remove(permission, permission, ...) myuser.user_permissions.clear()
除了那些自动 API 方法之外, User 对象还有以下自己的方法:
-
is_anonymous()
¶
-
总是返回 False 。这是一个区别User 和AnonymousUser 对象的方法。通常,你会更喜欢用is_authenticated() 来代替这个方法。
-
is_authenticated()
¶
-
总是返回 True 。这是一个测试用户是否经过验证的方法。这并不表示任何权限,也不测试用户是否是活动的。这只是验证用户是否合法,密码是否正确。
-
get_full_name()
¶
-
返回 first_name 加上last_name ,中间加上一个空格。
-
check_password(
raw_password)
¶
-
如果密码正确则返回 True 。(在比较密码时要注意哈希算法。)
-
set_unusable_password()
¶
-
标记用户可以不设置密码,这与用户使用空字符串作为密码是不同的。对这种用户, check_password() 肯定不会返回True 。不保存 User 对象。
如果你的应用存在于如 LDAP 目录之类的外部来源时,那么可能需要这个方法。
-
has_usable_password()
¶
-
如果对于这个用户调用 set_unusable_password() 则返回False 。
-
get_group_permissions(
obj=None)
¶
-
通过用户的组返回用户的一套权限字符串。
New in Django 1.2: Please, see the release notes如果有 obj 参数,则只返回这个特定对象的组权限。
-
get_all_permissions(
obj=None)
¶
-
通过用户的组和用户权限返回用户的一套权限字符串。
New in Django 1.2: Please, see the release notes如果有 obj 参数,则只返回这个特定对象的组权限。
-
has_perm(
perm,
obj=None)
¶
-
如果用户有特定的权限则返回 True ,这里的 perm 的格式为"<app label>.<permissioncodename>" 。(参见下面 权限 一节。)如果用户是不活动的,这个方法总是返回 False 。
New in Django 1.2: Please, see the release notes如果有 obj 参数,这个方法不会检查模型的权限,只会检查这个特定对象的权限。
-
has_perms(
perm_list,
obj=None)
¶
-
如果用户有列表中每个特定的权限则返回 True ,这里的 perm 的格式为"<app label>.<permissioncodename>" 。如果用户是不活动的,这个方法总是返回 False 。
New in Django 1.2: Please, see the release notes如果有 obj 参数,这个方法不会检查模型的权限,只会检查这个特定对象的权限。
-
has_module_perms(
package_name)
¶
-
如果用户在给定的包( Django 应用标签)中有任何一个权限则返回 True 。如果用户是不活动的,这个方法总是返回False 。
-
get_and_delete_messages()
¶
-
在用户的列表中获得 Message 对象列表,并从队列中删除。
-
email_user(
subject,
message,
from_email=None)
¶
-
发送一个电子邮件给用户。如果 from_email 为None ,则使用 DEFAULT_FROM_EMAIL 。
-
get_profile()
¶
-
返回用户的特定站点的描述。如果当前站点不提供描述则引发 django.contrib.auth.models.SiteProfileNotAvailable 。关于定义特定站点用户描述,参见下文储存用户的额外信息 一节。
-
管理器函数¶
-
class
models.
UserManager
¶
-
User 模型有一个包含以下实用函数的自定义管理器:
-
create_user(
username,
email,
password=None)
¶
-
创建、保存并返回一个 User 。
username 属性和 password 属性根据给出的参数设置。email 属性自动转换为小写字母,且返回的User 对象会有一个设置为True 的 is_active 属性。
如果没有提供密码,就会调用 set_unusable_password() 。
使用举例参见 创建用户 。
-
make_random_password(
length=10,
allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
¶
-
根据给定的长度和给定的可用字符串返回一个随机密码。注意缺省的可用字符串不包括以下容易混淆的字符:
- i 、 l 、 I 和1 (小写字母 i 、小写字母 L 、大写字母 i 和数字一)
- o 、 O 和 0 (大写字母 o 、小写字母 o 和零) and zero)
-
基本用法¶
创建用户¶
最基本的创建用户的方法是使用 Django 自带的 create_user() 助理函数:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# 到这里, user 是一个已存入数据库的 User 对象。如果要改变其它字段可以继续
# 改变这个对象的属性。
>>> user.is_staff = True
>>> user.save()
你也可以用 Django 的管理站点来创建用户。假设你已经启用了管理站点并把站点挂接到 /admin/ ,/admin/auth/user/add/ 页面会有一个“增加用户( Add user )” 按钮。在管理站点的主页面中的“认证” 小节中还可以看见一个“用户( Users )” 链接。“增加用户”页面与标准的管理站点页面不同之处在于在允许编辑其它用户字段之前必须确定用户名和密码。
同时要注意:在管理站点中,如果要你的帐户可以创建用户,那么必须有添加 和 变更用户的权限。如果只有添加用户权限而没变更权限,那么是不能创建用户的。因为如果有添加用户权限,那么就可以添加超级用户,然后就可以变更其他用户。所以 Django 需要同时具备添加和 变更权限。这是出于安全性考虑。
变更密码¶
manage.pychangepassword *username* 提供了一个用命令行方式变更用户密码的途径。如果给定了用户名,这个命令会提示你输入两次密码。当两次输入的密码相同时,该用户的新密码会立即生效。如果没有给定用户,这个命令会尝试改变与当前用户名匹配的用户的密码。
你也可以使用 set_password() 来变更密码:
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username__exact='john')
>>> u.set_password('new password')
>>> u.save()
不要直接设置 password 属性,除非必须。下一节会说明原因。
密码¶
User 对象的password 属性是一个如下格式的字符串:
hashtype$salt$hash
由哈希类型、盐值和哈希值组成,用美元符合分隔。
哈希类型可以是 sha1 (缺省值)、md5 或 crypt ,表明使用哪种单向哈希算法来处理密码。盐值是一个随机字符串,用于在哈希过程中加盐。注意 crypt 方法只适用于装有标准 Python crypt 模块的平台下。
密码例子:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
set_password() 方法和 check_password() 函数用于设置和检查密码。
以前版本的 Django ,如 0.90 版,只是简单使用 MD5 哈希,并且不使用盐值。为了向后兼容,这些密码还是被支持的,指定用户的密码在第一次正常使用check_password() 后,会自动转换为新的形式。
匿名用户¶
-
class
models.
AnonymousUser
¶
-
django.contrib.auth.models.AnonymousUser 是一个 django.contrib.auth.models.User 接口实现的类,区别如下:
- id 总是None 。
- is_staff 和 is_superuser 都总是False 。
- is_active 总是 False 。
- groups 和user_permissions 都总是空的。
- is_anonymous() 返回 True ,而不是 False 。
- is_authenticated() 返回 False ,而不是 True 。
- set_password() 、 check_password() 、save() 、delete() 、set_groups() 和set_permissions() 引发NotImplementedError 。
在实践中,你一般不会自己使用 AnonymousUser 对象,但是 Web 请求会使用,在下节会详述。
创建超级用户¶
在 INSTALLED_APPS 中添加'django.contrib.auth' 后第一次运行manage.pysyncdb 会提示你创建一个超级用户。如何在以后单独创建可以使用以下命令行工具:
manage.py createsuperuser --username=joe --email=joe@example.com
运行后会提供输入密码。输入密码后,用户会被立即创建。如果没有写明 --username 或--email ,会提示你输入这两个值。
如果你使用的是老版本的 Django ,则可以使用如下命令:
python /path/to/django/contrib/auth/create_superuser.py
...这里的 /path/to 是你系统中 Django 代码所在路径。推荐使用manage.py 命令,因为它为你提供了恰当的路径和环境。
为用户储存额外的信息¶
如果要储存用户相关的额外信息, Django 提供了一个方法来定义一个站点相关模型,称之为“用户概况( user profile )”。
具体实现的方式是定义一个用于储存额外信息或附加方法的模型,同时在模型中添加一个 OneToOneField 关联到 User 模型。这样就可以保证每个User 只能创建唯一的模型实例。
为了使定义的模型成为站点的用户概况模型,必须有 AUTH_PROFILE_MODULE 设置中定义一个包含以下条目的字符串,条目间以点号分隔:
- 使用用户概况模型的应用名称(大小写敏感)。亦即用来创建应用而传递给 manage.pystartapp 的名称。
- 模型的名称(大小写不敏感)。
例如,如果用户概况模型名称为 UserProfile 且定义在一个名称为accounts 的应用中,那么正确的设置是:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
当一个用户概况模型定义好并按上述方法指定之后,每个 User 对象就会有一个get_profile() 方法,这个方法可以返回与User 相关联的用户概况模型。
如果用户概况不存在,则 get_profile() 方法不会创建用户概况。你必需在用户模型中为django.db.models.signals.post_save 信号注册一个句柄,并且在句柄中,如果 created=True ,则创建相关的用户概况。
更多信息,参见 Django 之书第十二章.
Web 请求中的认证¶
到这里本文谈得是手动处理认证相关对象的低级 API ,在更高的级别 Django 可以把这个认证框架挂接到 request objects 系统中。
首先,通过 MIDDLEWARE_CLASSES 设置安装SessionMiddleware 和AuthenticationMiddleware 中间件。详见session documentation 。
安装完中间件后就可以在视图中操作 request.user 了。request.user 会给你一个User 对象来表现当前已登录的用户。如果用户还没有登录,那么request.user 会被设置为一个AnonymousUser 的实例(参见前一节)。你可以用于is_authenticated() 来区别对待,如下例:
if request.user.is_authenticated():
# 做一些已登录用户的事。
else:
# 做一些匿名用户的事。
如何登录一个用户¶
Django 在 django.contrib.auth 模型中提供两个函数:authenticate() 和login() .
-
authenticate()
¶
-
authenticate() 用于验证指定用户的用户名和密码。这个函数有两个关键字参数,username 和 password ,如果密码与用户匹配则返回一个 User 对象。否则返回None 。例如:
from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: if user.is_active: print "你提供了一个正确的用户名和密码!" else: print "帐户已禁用!" else: print "用户名或密码不正确。"
-
login()
¶
-
login() 用于在视图中登录用户。它带有一个HttpRequest 对象和一个User 对象。login() 使用 Django 的会话框架在会话中保存用户的 ID 。因此如前文所述,会话中间件必须已经安装。
本例展示如何同时使用 authenticate() 和login():
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) # 重定向到一个登录成功页面。 else: # 返回一个“帐户已禁用”错误信息。 else: # 返回一个“非法用户名或密码”错误信息。
首先调用 authenticate()
当你手动登录一个用户时, 必须 在调用 login() 之前调用authenticate() 。在成功验证用户后,authenticate() 会在User 上设置一个空属性(详见其他认证资源 ),这个信息在以后登录过程中要用到。
手动检查一个用户的密码¶
-
check_password()
¶
-
如果要通过比较明文密码和数据库中的哈希密码来手动认证用户,可以使用便捷的 django.contrib.auth.models.check_password() 函数。它带有两个参数:要比较的明文密码和被比较的数据库中用户密码 字段的完整值。如果匹配则返回 True ,否则返回 False 。
如何登出一个用户¶
-
logout()
¶
-
在视图中可以使用 django.contrib.auth.logout() 来登出通过django.contrib.auth.login() 登录的用户。它带有一个HttpRequest 对象,没有返回值。例如:
from django.contrib.auth import logout def logout_view(request): logout(request) # 重定向到一个登出成功页面。
注意当用户没有登录时,调用 logout() 函数不会引发任何错误。
当调用 logout() 时,当前请求的会话数据会清空。这是为了防止另一个用户使用同一个浏览器登录时会使用到前一个用户的会话数据。如果要在用户登出后有会话中储存一些数据,那么得在django.contrib.auth.logout()之后 储存。
登录和登出信号¶
当一个用户登录或登出时认证框架使用两个用于通知的 信号 。
-
django.contrib.auth.signals.
user_logged_in
¶
成功登录时所发的信号。
这个信号的参数:
-
sender
- 同上,登录成功的用户类。 request
- 当前 HttpRequest 实例。 user
- 登录成功的用户实例。
-
django.contrib.auth.signals.
user_logged_out
¶
调用登出方法时发的信号。
-
sender
- 同上:登出的用户类或者 None (用户验证失败时)。 request
- 当前 HttpRequest 实例。 user
- 登出成功的用户实例或者 None (用户验证失败时)。
控制非登录用户的页面权限¶
原始方式¶
页面限制的简单原始方式是检查 request.user.is_authenticated() 并且重定向到一个登录页面:
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
# ...
...或显示一个出错页面:
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
# ...
登录要求饰件¶
-
decorators.
login_required(
[
redirect_field_name=REDIRECT_FIELD_NAME,
login_url=None
])
¶
-
做为一个捷径,可以使用方便的 login_required() 饰件:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required() 做以下事情:
- 如果用户没有登录,那么就重定向到 settings.LOGIN_URL ,并在查询字符串中传递当前绝对路径。例如:/accounts/login/?next=/polls/3/ 。
- 如果用户已登录,则正常的执行视图。视图代码认为用户已登录。
缺省情况下,成功登录后重定向的路径是保存在 next 参数中的。如果你想用另一个名称,那么login_required() 可以使用一个可选的redirect_field_name 参数:
from django.contrib.auth.decorators import login_required @login_required(redirect_field_name='my_redirect_field') def my_view(request): ...
注意,如果你提供了一个值给 redirect_field_name ,那么你最好也同样自定义你的登录模板。因为保存重定向路径的模板环境变量会使用redirect_field_name 作为关键值,而不使用缺省的"next" 。
New in Django Development version.login_required() 还有一个可选的 login_url 参数。例如:
from django.contrib.auth.decorators import login_required @login_required(login_url='/accounts/login/') def my_view(request): ...
注意,如果你没有指定 login_url 参数,那么你需要为settings.LOGIN_URL 映射适当的视图。例如,在缺省情况下,在你的 URLconf 中加入以下一行:
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
-
views.
login(
request
[,
template_name,
redirect_field_name,
authentication_form
])
¶
-
以下是 django.contrib.auth.views.login 做了什么:
- 如果是通过 GET 调用的,则显示一个 POST 到相同 URL 的登录表单。稍后会以更多的说明。
- 如果是通过 POST 调用的,则会尝试登录用户。如果登录成功,则会重定向到next 指定的 URL 。如果 next 没有提供,则重定向到 settings.LOGIN_REDIRECT_URL (缺省的是/accounts/profile/ )。如果登录不成功,则重新显示登录表单。
缺省情况下,在名为 registration/login.html 的模板中提供一个登录表单是你的责任。这个模板要传递四个环境变量:
- form: 一个表现登录表单的 Form 对象。关于Form 对象的更多内容 表单文档 。
- next: 登录成功后要重定向的 URL 。也可以包含一个查询字符串。
- site: 当前的 Site ,依赖于SITE_ID 设置。如果没有安装站点框架,则会被设置为一个RequestSite 实例,这个实例会使用从当前HttpRequest 中派生出的站点名称和域名。
- site_name: site.name 的一个别名。如果没有安装站点框架,则会被设置为 request.META['SERVER_NAME'] 的值。更多关于站点的内容参见The "sites" framework 。
如果你不想调用模板文件 registration/login.html ,那么可以在 URLconf 通过额外参数把template_name 传递给视图。例如,以下的 URLconf 设置会使用myapp/login.html 来代替缺省的模板:
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
你还可以通过传递 redirect_field_name 给视图来指定包含登录后重定向 URL 的GET 字段的名称。这个字段缺省值为 next 。
这里有一个例子模板文件 registration/login.html ,你可以作为一个基础。它假设你有一个base.html 模板,模板中定义了一个content 块:
{% extends "base.html" %} {% load url from future %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} <form method="post" action="{% url 'django.contrib.auth.views.login' %}"> {% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> <td>{{ form.username }}</td> </tr> <tr> <td>{{ form.password.label_tag }}</td> <td>{{ form.password }}</td> </tr> </table> <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next }}" /> </form> {% endblock %}
New in Django 1.2: Please, see the release notes如果你使用是其它认证方式(参见 其他认证资源 ),那么可以通过 authentication_form 参数把自定义的认证表单传递给登录视图。这个表单必须在它的__init__ 方法接受一个 request 关键字参数,并且提供一个用于返回认证用户对象的 get_user 方法(这个方法只会在表单验证成功后调用)。
其他内建视图¶
除了 login() 视图外,认证系统还有其他一些有用的内建视图。这些视图都在django.contrib.auth.views 中:
-
logout(
request
[,
next_page,
template_name,
redirect_field_name
])
¶
-
登出用户。
可选参数:
- next_page: 登出后转向的 URL 。
- template_name: 登出后要显示的模板的全名。如果没有提供这个参数,缺省为registration/logged_out.html 。
- redirect_field_name: 包含登出后转向的 URL 的GET 字段的名称。如果给出则覆盖 next_page 。
模板语境:
- title: 本地化的 "Logged out" 字符串。
-
logout_then_login(
request
[,
login_url
])
¶
-
登出用户,并重定向到登录页面。
可选参数:
- login_url: 要重定向到的登录页面的 URL 。如果没有给出这个参数,则缺省为settings.LOGIN_URL 。
-
password_change(
request
[,
template_name,
post_change_redirect,
password_change_form
])
¶
-
允许一个用户更改自己的密码。
可选参数:
-
template_name: 显示用户更改页面的模板全名。如果没有给出,则缺省为registration/password_change_form.html 。
-
post_change_redirect: 成功更改密码后要转向到的 URL 。
-
New in Django 1.2: Please, see the release notes
password_change_form: 一个自定义的“更改密码”的表单,必须接受一个user 关键字参数。这个表单负责改变用户密码。
模板语境:
- form: 密码变更表单。
-
-
password_change_done(
request
[,
template_name
])
¶
-
用户变更密码后要显示的页面。
可选参数:
- template_name: 模板的全名。缺省为registration/password_change_done.html 。
-
password_reset(
request
[,
is_admin_site,
template_name,
email_template_name,
password_reset_form,
token_generator,
post_reset_redirect,
from_email
])
¶
-
生成一个用于用户重置密码的一次性链接,并通过电子邮件发送这个链接。
Changed in Django Development version: 增加了 from_email 参数。可选参数:
- template_name: 用于显示密码重置页面的模板全名。如果没有给出,则缺省为registration/password_reset_form.html 。
- email_template_name: 用于生成包含新密码的电子邮件的页面的模板全名。如果没有给出,则缺省为registration/password_reset_email.html 。
- password_reset_form: 用于设置密码的表单。缺省为PasswordResetForm 。
- token_generator: 检查密码的类的实例。缺省为default_token_generator ,它是一个django.contrib.auth.tokens.PasswordResetTokenGenerator 的实例。
- post_reset_redirect: 成功变更密码后要转向的 URL 。
- from_email: 一个可用的电子邮件地址。缺省情况下 Django 使用DEFAULT_FROM_EMAIL 。
模板语境:
- form: 用于重置密码的表单。
-
password_reset_done(
request
[,
template_name
])
¶
-
重置密码显示的页面。
可选参数:
- template_name: 模板的全名。缺省为registration/password_reset_done.html 。
-
redirect_to_login(
next
[,
login_url,
redirect_field_name
])
¶
-
重定向到登录页面,成功登录后回到另一个 URL 。
必选参数:
- next: 成功登录后到转向的 URL 。
可选参数:
- login_url: 登录页面的 URL 。缺省为settings.LOGIN_URL 。
- redirect_field_name: 包含登录后要转向的 URL 的GET 字段的名称。如果给出则覆盖 next 。
-
password_reset_confirm(
request
[,
uidb36,
token,
template_name,
token_generator,
set_password_form,
post_reset_redirect
])
¶
-
给出一个输入新密码的表单。
可选参数:
- uidb36: 用户的 id 以 base 36 方式编码。缺省为None 。
- token: 检查密码是否有效的标志。缺省为None.
- template_name: 显示确认密码视图的模板的全名。缺省为registration/password_reset_confirm.html 。
- token_generator: 检查密码的类的实例。缺省为default_token_generator ,它是一个django.contrib.auth.tokens.PasswordResetTokenGenerator 的实例。
- set_password_form: 用于设置密码的表单。缺省为SetPasswordForm 。
- post_reset_redirect: 重置密码的要转向的 URL 。缺省为None 。
-
password_reset_complete(
request
[,
template_name
])
¶
-
展现一个通知用户密码已成功变更的视图。
可选参数:
- template_name: 显示视图的模板的全名。缺省为registration/password_reset_complete.html 。
内建表单¶
如果你不想使用内建的视图,但是想自己写表单,那么认证系统在 django.contrib.auth.forms 中提供了一些内建的表单:
-
class
AdminPasswordChangeForm
¶
-
在管理接口中改变用户密码的表单。
-
class
AuthenticationForm
¶
-
登录表单。
-
class
PasswordChangeForm
¶
-
变更密码表单。
-
class
PasswordResetForm
¶
-
生成重置密码链接并通过电子邮件发送链接的表单。
-
class
SetPasswordForm
¶
-
不需要输入旧密码就可以变更用户密码的表单。
-
class
UserChangeForm
¶
-
在管理接口中变更用户信息和权限的表单。
-
class
UserCreationForm
¶
-
创建新用户的表单。
通过测试来限制页面操作¶
要基于一定的权限或测试来限制页面的操作,其本质上和上一节的内容相同。
最简单的方式是在社图中直接在 request.user 上直接运行你的测试。例如,这个视图调试用户是否已登录并拥有polls.can_vote 权限:
def my_view(request):
if not request.user.has_perm('polls.can_vote'):
return HttpResponse("在这个选举中你不能投票。")
# ...
-
user_passes_test()
¶
-
作为一个捷径,你可使用方便的 user_passes_test 装饰件:
from django.contrib.auth.decorators import user_passes_test @user_passes_test(lambda u: u.has_perm('polls.can_vote')) def my_view(request): ...
我们使用这个特定的测试作为一个相关的简单的例子。然而,如果你只要测试一个用户是否有一个权限,那么你可以使用 permission_required() 装饰件,稍后会谈到这个装饰件。
user_passes_test() 有一个必选参数:带有一个 User 对象的可调用的函数,并且如果用户被允许查看页面时返回True 。注意 user_passes_test() 不自动检查User 是否是匿名用户。
user_passes_test() 有一个可选的 login_url 参数,这个参数让你指定登录页面的 URL (缺省为settings.LOGIN_URL )。
例如:
from django.contrib.auth.decorators import user_passes_test @user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/') def my_view(request): ...
权限需求装饰件¶
-
permission_required()
¶
-
检查用户是否有特定的权限是一个比较常见的任务。因此, Django 为此提供了一个捷径: permission_required() 装饰件。使用这个装饰件,先前的例子可以写成:
from django.contrib.auth.decorators import permission_required def my_view(request): ... my_view = permission_required('polls.can_vote')(my_view)
至于 User.has_perm() 方法,权限名称以"<app label>.<permissioncodename>" 形式组成(例如 polls.can_vote 就是poll 应用中的模型中的一个权限)。
注意 permission_required() 也有一个可选的login_url 参数。例如:
from django.contrib.auth.decorators import permission_required def my_view(request): ... my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
在 login_required() 装饰件中login_url 缺省为 settings.LOGIN_URL 。
权限¶
Django 自带了一个简单的权限系统,它可以管理特定的用户和用户组的权限。
这个系统是用于 Django 管理站点的,但是欢迎在你自己的代码中使用它。
Django 管理站点使用如下权限:
- 查看“增加”表单和增加对象需要有该种对象的“增加”权限。
- 查看变更列表、“变更”表单和变更对象需要有该种对象的“变更”权限。
- 删除对象需要有该种对象的“删除”权限。
权限是针对某种对象的全局性的设置,而不是针对某种对象的某个特定实例的。例如,我们可以说“玛丽可以改写故事”,但是不能说“玛丽可以改写故事,但只限于她自己的故事”,也不能说“玛丽只能改写符合特定条件(如出版日期、编号)的故事”。后面的两种情况是 Django 的开发者正在讨论的东西,现在还不支持。
缺省权限¶
当 django.contrib.auth 存在于你的INSTALLED_APPS 设置中时,会确保你安装的应用中每个 Django 模型在三个缺省的权限——增加、变更和删除。
当你运行 manage.pysyncdb 时这些权限会被创建。在向 INSTALLED_APPS 增加django.contrib.auth 后第一次运行syncdb 时,会为以前已安装的模型和正在安装的模型创建缺省权限。以后每次运行manage.pysyncdb 都会为所有模型创建缺省权限。
假设你的应用有一个名为 foo 的 app_label 和一个名为Bar 的模型,要测试基本的权限可以这样:
* add: ``user.has_perm('foo.add_bar')``
* change: ``user.has_perm('foo.change_bar')``
* delete: ``user.has_perm('foo.delete_bar')``
自定义权限¶
使用 权限 模型元属性 可以创建自定义权限。
下例中的 Task 模型创建了三个自定义权限:
class Task(models.Model):
...
class Meta:
permissions = (
("can_view", "Can see available tasks"),
("can_change_status", "Can change the status of tasks"),
("can_close", "Can remove a task by setting its status as closed"),
)
当运行 manage.pysyncdb 时以上代码只做了一件事:创建了三个额外的权限。当用户尝试操作应用提供的功能(查看任务、改变任务状态和关闭任务)时,你的代码必须负责检查权限的值。
API 手册¶
-
class
models.
Permission
¶
-
和用户一样,权限也在 django/contrib/auth/models.py Django 模型中实现。
模板中的认证数据¶
当你使用 RequestContext 时,当前已登录的用户及其权限在模板环境 中提供。
技术细节
在技术细节上,这些变量只有当你使用 RequestContext ,并且 你的 TEMPLATE_CONTEXT_PROCESSORS 设置中包含"django.contrib.auth.context_processors.auth" 时才在模板环境中可用,这是缺省 情况。更多见容,详见请求环境文档 。
用户¶
当渲染一个模板 RequestContext 时,当前已登录的用户(要么是一个User 实例,要么是一个AnonymousUser 实例)被储存在模板变量{{ user}} 中:
{% if user.is_authenticated %}
<p>欢迎, {{ user.username }} 。谢谢登录。</p>
{% else %}
<p>欢迎,新用户。请登录。</p>
{% endif %}
如果一个 RequestContext 未使用则这个模板环境变量不可用。
权限¶
当前已登录用户的权限储存在模板变量 {{ perms }} 中。这是一个 django.contrib.auth.context_processors.PermWrapper 的实例。它是一个模板友好的权限的代理。
在 {{ perms}} 对象中,单个属性的查找是 User.has_module_perms 方法的一个代理。
如果已登录用户在 foo 应用中有权限,那么以下例子会显示True
{{ perms.foo }}
二维属性查找是 User.has_perm 方法的一个代理。如果已登录用户有foo.can_vote 权限,那么以下例子会显示True
{{ perms.foo.can_vote }}
然而,你可以在模板 {% if %} 语句中检查权限:
组¶
组是一种把用户分类以便于分配权限或打上标签的常用方式。一个用户可以属于任意多个组。
组中的用户自动被赋予这个组所拥有的权限。例如,如果 网站编辑 组拥有can_edit_home_page 权限,那么这个组中的用户都有这个权限。
除了权限外,组还是一个把用户分类以便于打上标签或给予特定功能的便捷方式。例如,你可创建一个 特别用户 组,然后就可以为这个编写特定的程序,给予站点特定的部分的全员专有访问权限或发送会员专有电子邮件等等。
消息¶
消息系统是一个指定用户的消息队列的轻度实现。
每个消息与一个 User 相关联。消息没有期限或时间戳。
消息用于 Django 管理站点提示某个动作已成功完成。例如 "投票已成功创建" 就是一个消息。
API 很简单:
-
models.User.message_set.
create(
message)
¶
-
使用 user_obj.message_set.create(message='message_text') 可以创建一个新消息。
取得或删除消息使用 user_obj.get_and_delete_messages()<django.contrib.auth.models.User.get_and_delete_messages>`。这个方法从用户的消息队列中取回(如果有消息的话)一个 ``消息`() 对象的列表并在队列中删除这些消息。
在以下的例子视图中,系统为用户保存了创建节目单后产生的一个消息:
def create_playlist(request, songs):
# Create the playlist with the given songs.
# ...
request.user.message_set.create(message="你的节目单已成功添加。")
return render_to_response("playlists/create.html",
context_instance=RequestContext(request))
当你使用 RequestContext 时,当前的已登录用户及其消息可由模板环境 中模板变量 {{ messages }} 来提供。下面就是显示消息的模板例子:
最后,注意消息框架只能用于用户数据库中已存在的用户。如果要向匿名用户发送消息,请使用 消息框架 。
其他认证资源¶
Django 自带的认证系统对于一般情况已经够用了,但是你可能需要挂接其他的认证资源,即另一个用户名和密码资源或认证方法。
例如,你的公司可能已有一个 LDAP ,其中已储存了每个员工的用户名和密码。如果在 LDAP 和 基于 Django 的应用中使用两套帐户,那么对于管理员和用户来说都是不方便的。
因此,为了应对这种情况, Django 的认证系统可以让你挂接其他认证资源。你可以重载 Django 的缺省数据库计划或使缺省系统与其他系统协作。
关于 Django 认证后台处理的内容参见 认证后台参考 。
指定认证后端¶
在后台, Django 维护着一个用于认证的“认证后端”列表。当象前文 如何登录一个用户 一节中提到的,使用 django.contrib.auth.authenticate() 时, Django 会尝试列表中的所有后端。如果第一个不行,则尝试第二个,以此类推,直到最后一个。
认证后端的列表中 AUTHENTICATION_BACKENDS 设置中定义。设置内容应当是一个指向可以用于认证的 Python 类的路径名的元组。这些类可以放在 Python 路径下的任意位置。
缺省情况下, AUTHENTICATION_BACKENDS 被设置为:
('django.contrib.auth.backends.ModelBackend',)
这是检查 Django 用户数据库的基本认证方案。
AUTHENTICATION_BACKENDS 的顺序是重要的,所以如果相同的用户名和密码在多个后端中同时存在, Django 会在第一个匹配的地方停止。
Note
一旦一个用户通过认证, Django 会在会话中储存该用户所使用的后端,并且在随后的认证中使用已储存的后端。即认证资源会被缓存,如果你改变AUTHENTICATION_BACKENDS ,并且要强迫用户使用不同的后端重新认证,那么需要清除会话数据。一个简单的方法是执行Session.objects.all().delete() 。
编写一个认证后端¶
一个认证后端是一个执行两个方法的类: get_user(user_id) 和authenticate(**credentials) 。
get_user 方法需要一个 user_id (可以是一个用户名、数据库 ID 或其他东东),并且返回一个 用户 对象。
authenticate 方法有一个证书关键字参数。多数情况下看上去如下:
class MyBackend:
def authenticate(self, username=None, password=None):
# 检查用户名和密码并返回一个用户。
但是也可以认证一个标志,象下面这样:
class MyBackend:
def authenticate(self, token=None):
# 检查标志并返回一个用户。
不管怎样, authenticate 应当检查得到的材料。当材料有效时,返回一个符合条件的用户 对象,否则返回 None 。
Django 管理系统与本文开头所谈的 Django 用户 对象是紧密关联的。目前,最好的办法是为你的认证后端中存在的每个用户创建一个 Django用户 对象(例如,在你的 LDAP 目录中,你的 SQL 数据库中等等)。你要么写一个脚本,要么你的认证 方法在第一次用户登录时来创建 用户 对象。
以下是一个后端举例,它检查 settings.py 文件中定义的用户名和密码,并且在用户第一次登录时创建一个用户 对象:
from django.conf import settings
from django.contrib.auth.models import User, check_password
class SettingsBackend:
"""
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'
"""
supports_object_permissions = False
supports_anonymous_user = False
supports_inactive_user = False
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
在自定义后端中处理认证¶
自定义后端可以提供自己的权限。
用户模型会把权限查找函数(:meth:~django.contrib.auth.models.User.get_group_permissions() 、get_all_permissions() 、has_perm() 和has_module_perms() )委托给执行这些函数的任何一个后端。
提供给用户的权限将会是所有后端返回的权限的超集。即 Django 会把任一个后端的权限都提供给用户。
上面的简单的后端可以相当简单的执行神奇的权限管理:
class SettingsBackend:
# ...
def has_perm(self, user_obj, perm):
if user_obj.username == settings.ADMIN_LOGIN:
return True
else:
return False
上例中通过验证的用户得到了所有权限。注意,后端认证函数都把用户对象作为一个参数并且接受与 django.contrib.auth.models.User 同样的参数。
一个完整的认证实现参见 django/contrib/auth/backends.py 。通常情况下这是缺省后端,查询的是 auth_permission 表。
匿名用户认证¶
匿名用户是指没有经过认证的用户,如没有提供有效认证信息的用户。但是不等于说匿名用户就什么事也不能做。多数网站可以允许匿名用户浏览大多数网页,而且很多网站还允许匿名用户发表评论等。
Django 认证框架中没有地方储存匿名用户的权限,但是它允许自定义认证后端来处理匿名用户的认证。这个能力在重用应用时尤其有用,这样不用设置就可以解决认证后端的所有问题。
在你的认证后端中要启用匿名用户认证,必须把类属性 supports_anonymous_user 设置为True 。(这样是为了认证后端的兼容性,确保所有用户对象都是django.contrib.auth.models.User 类的真正实例。)这样,django.contrib.auth.models.AnonymousUser 就会把相应的权限方法委派给认证后端。
在 Django 1.2 中如果不存在 supports_anonymous_user 属性会引发一个隐藏的PendingDeprecationWarning 警告。在 Django 1.3 中,这个警告会升级为一个显眼的DeprecationWarning 警告。另外,supports_anonymous_user 会被设置为False 。 Django 1.4 会假设每一个后端都支持把匿名用户传递给认证方法。
非活动用户的认证¶
一个非活动用户是指已经被认证过但是 is_active 属性为False 的用户。但是非活动用户是经过认证的,还是有一定权限的,比如他们被允许激活他们的帐户。
在权限系统中因为有对匿名用户的支持,所以匿名用户有一定的权限而不活动用户则没有。
要使非活动用户有一定权限,必须把类属性 supports_inactive_user 设置为True 。
在 Django 1.3 中如果不存在 supports_inactive_user 属性会引发一个PendingDeprecationWarning 警告。在 Django 1.4 中,这个警告会升级为一个显眼的DeprecationWarning 警告。另外,supports_inactive_user 会被设置为False 。 Django 1.5 会假设每一个后端都支持把非活动用户传递给认证方法。
处理对象权限¶
Django 的权限框架支持对象权限。但是不是在框架的核心中实现的,这就意味着在检查对象权限时总是返回 False 或一个空列表(取决于如何检查)。
要在你自己的 认证后端 中使用对象权限,只要允许向权限传递一个 obj 参数并且设置supports_object_permissions 类属性为True 。
在 Django 1.2 中一个不存在的 supports_object_permissions 属性会引发一个隐藏的PendingDeprecationWarning 。在 Django 1.3 中,这个警告会升级为一个DeprecationWarning ,非常显眼。附加的supports_object_permissions 会被设置为False 。 Django 1.4 会假设每一个后端都支持对象权限并且不会检查supports_object_permissions 是否存在,意即不支持obj 作为一个参数就会引发一个 TypeError 。