用户&Authentication
Django 用户认证系统处理用户帐号,组,权限以及基于cookie的用户会话。 这个系统一般被称为 auth/auth (认证与授权)系统。 这个系统的名称同时也表明了用户常见的两步处理。
-
验证 (认证) 用户是否是他所宣称的用户(一般通过查询数据库验证其用户名和密码)
-
验证用户是否拥有执行某种操作的 授权 (通常会通过检查一个权限表来确认)
根据这些需求,Django 认证/授权 系统会包含以下的部分:
-
用户 : 在网站注册的人
-
权限 : 用于标识用户是否可以执行某种操作的二进制(yes/no)标志
-
组 :一种可以将标记和权限应用于多个用户的常用方法
-
Messages : 向用户显示队列式的系统消息的常用方法
打开认证支持
-
将 'django.contrib.auth' 放在你的 INSTALLED_APPS 设置中,然后运行 manage.py syncdb以创建对应的数据库表。
-
确认 SessionMiddleware 后面的 MIDDLEWARE_CLASSES 设置中包含 'django.contrib.auth.middleware.AuthenticationMiddleware' SessionMiddleware。
使用User对象
通过以上步骤,我们就可以在视图(view)的函数中处理user了。 在视图中存取users,主要用 request.user ;这个对象表示当前已登录的用户。 如果用户还没登录,这就是一个AnonymousUser对象(细节见下)。你可以很容易地通过 is_authenticated() 方法来判断一个用户是否已经登录了:
if request.user.is_authenticated():
# Do something for authenticated users.
else:
# Do something for anonymous users.
- User属性
password 必需的。 密码的哈希值(Django不储存原始密码)。
is_staff 布尔值。 用户是否拥有网站的管理权限。
is_active 布尔值。 设置该账户是否可以登录,把该标志位置为False而不是直接删除账户。
is_superuser 布尔值 标识用户是否拥有所有权限,无需显式地权限分配定义。
last_login 用户上次登录的时间日期。 它被默认设置为当前的日期/时间。
date_joined 账号被创建的日期时间 当账号被创建时,它被默认设置为当前的日期/时间。
- User关联对象
User
对象有两个many-to-many属性。 groups
和permissions
。正如其他的many-to-many属性使用的方法一样,User
对象可以获得它们相关的对象:
# Set a user's groups:
myuser.groups = group_list
# Add a user to some groups:
myuser.groups.add(group1, group2,...)
# Remove a user from some groups:
myuser.groups.remove(group1, group2,...)
# Remove a user from all groups:
myuser.groups.clear()
# Permissions work the same way
myuser.permissions = permission_list
myuser.permissions.add(permission1, permission2, ...)
myuser.permissions.remove(permission1, permission2, ...)
myuser.permissions.clear()
登录&登出
from django.contrib import auth
def login_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
# Correct password, and the user is marked "active"
auth.login(request, user)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedin/")
else:
# Show an error page
return HttpResponseRedirect("/account/invalid/")
from django.contrib import auth
def logout_view(request):
auth.logout(request)
# Redirect to a success page.
return HttpResponseRedirect("/account/loggedout/")
在实际中,你一般不需要自己写登录/登出的函数;认证系统提供了一系例视图用来处理登录和登出。 使用认证视图的第一步是把它们写在你的URLconf中。
from django.contrib.auth.views import login, logout
urlpatterns = patterns('',
# existing patterns here...
(r'^accounts/login/$', login),
(r'^accounts/logout/$', logout),
)
#/accounts/login/ 和 /accounts/logout/ 是Django提供的视图的默认URL
如果用户登录成功,缺省会重定向到 /accounts/profile 。 你可以提供一个保存登录后重定向URL的next隐藏域来重载它的行为。
logout视图有一些不同。 默认情况下它渲染 registration/logged_out.html 模板,视图中还可以包含一个参数 next_page 用于退出后重定向。
访问限制
检查 request.user.is_authenticated() ,然后重定向到登陆页面:
from django.http import HttpResponseRedirect
def my_view(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/accounts/login/?next=%s' % request.path)
或者显示一个出错信息:
def my_view(request):
if not request.user.is_authenticated():
return render_to_response('myapp/login_error.html')
使用便捷的 login_required 修饰符:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
login_required 做下面的事情:
-
用户没有登录, 重定向到 /accounts/login/ , 把当前绝对URL作为 next 在查询字符串中传递过去, 例如: /accounts/login/?next=/polls/3/
-
如果用户已经登录, 正常地执行视图函数
对通过测试的用户限制访问
def vote(request):
if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')):
# vote here
else:
return HttpResponse("You can't vote in this poll.")
#Django有一个称为 user_passes_test 的简洁方式。它接受参数然后为你指定的情况生成装饰器。
def user_can_vote(user):
return user.is_authenticated() and user.has_perm("polls.can_vote")
@user_passes_test(user_can_vote, login_url="/login/")
def vote(request):
# Code here can assume a logged-in user with the correct permission.
- user_passes_test 使用一个必需的参数: 一个可调用的方法,当存在 User 对象并当此用户允许查看该页面时返回 True 。user_passes_test 不会自动检查 User
是否认证,你应该自己做这件事。 - 第二个可选的参数 login_url ,它让你指定你的登录页面的URL(默认为 /accounts/login/ )。 如果用户没有通过测试,那么user_passes_test将把用户重定向到login_url
Django为这种情形提供了一个捷径: permission_required() 装饰器。 使用这个装饰器,前面的例子可以改写为:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# ...
- permission_required() 也有一个可选的 login_url 参数, 这个参数默认为 '/accounts/login/'
限制通用视图的访问
在Django用户邮件列表中问到最多的问题是关于对通用视图的限制性访问。 为实现这个功能,你需要自己包装视图,并且在URLconf中,将你自己的版本替换通用视图:
from django.contrib.auth.decorators import login_required
from django.views.generic.date_based import object_detail
@login_required
def limited_object_detail(*args, **kwargs):
return object_detail(*args, **kwargs)
当然, 你可以用任何其他限定修饰符来替换 login_required 。
管理 Users, Permissions 和 Groups
当你需要绝对的控制权的时候,低层 API 需要深入专研,我们将在下面的章节中讨论它们。
创建用户
使用 create_user 辅助函数创建用户:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user(username='john',
... email='jlennon@beatles.com',
... password='glass onion')
>>> user.is_staff = True
>>> user.save()
修改密码
你可以使用 set_password() 来修改密码:
>>> user = User.objects.get(username='john')
>>> user.set_password('goo goo goo joob')
>>> user.save()
处理注册
每个开发人员都希望实现各自不同的注册方法,所以Django把写注册视图的工作留给了你,作为这个事情的最简化处理, 我们可以提供一个小视图, 提示一些必须的用户信息并创建这些用户。 Django为此提供了可用的内置表单, 下面这个例子就使用了这个表单:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/books/")
else:
form = UserCreationForm()
return render_to_response("registration/register.html", {
'form': form,
})
这个表单需要一个叫 registration/register.html 的模板。这个模板可能是这样的:
{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<h1>Create an account</h1>
<form action="" method="post">
{{ form.as_p }}
<input type="submit" value="Create the account">
</form>
{% endblock %}
在模板中使用认证数据
当前登入的用户以及他(她)的权限可以通过 RequestContext 在模板的context中使用。从技术上来说,只有当
- 使用了 RequestContext这些变量才可用。
- TEMPLATE_CONTEXT_PROCESSORS 设置包含了 “django.core.context_processors.auth” (默认情况就是如此)
这些变量才能在模板context中使用。
当使用 RequestContext 时, 当前用户 (是一个 User 实例或一个 AnonymousUser 实例) 存储在模板变量 {{ user }} 中:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
这些用户的权限信息存储在 {{ perms }} 模板变量中。
你有两种方式来使用 perms 对象。 你可以使用类似于 {{ perms.polls }} 的形式来检查,对于某个特定的应用,一个用户是否具有 任意 权限;你也可以使用 {{ perms.polls.can_vote }} 这样的形式,来检查一个用户是否拥有特定的权限。
{% if perms.polls %}
<p>You have permission to do something in the polls app.</p>
{% if perms.polls.can_vote %}
<p>You can vote!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the polls app.</p>
{% endif %}
权限、组和消息
权限
Django的admin站点如下使用权限:
-
只有设置了 add 权限的用户才能使用添加表单,添加对象的视图。
-
只有设置了 change 权限的用户才能使用变更列表,变更表格,变更对象的视图。
-
只有设置了 delete 权限的用户才能删除一个对象。
权限是根据每一个类型的对象而设置的,并不具体到对象的特定实例。 例如,我们可以允许Mary改变新故事,但是目前还不允许设置Mary只能改变自己创建的新故事,或者根据给定的状态,出版日期或者ID号来选择权限。
会自动为每一个Django模型创建三个基本权限:增加、改变和删除。 当你运行manage.py syncdb命令时,这些权限被添加到auth_permission数据库表中。
权限以 "<app>.<action>_<object_name>" 的形式出现。
权限也就是Django模型中的*** django.contrib.auth.models*** ,你也可以通过Django的数据库API直接操作权限。
组
组提供了一种通用的方式来让你按照一定的权限规则和其他标签将用户分类。 一个用户可以隶属于任何数量的组。在一个组中的用户自动获得了赋予该组的权限。 例如, Site editors 组拥有 can_edit_home_page 权限,任何在该组中的用户都拥有这个权限。
组也可以通过给定一些用户特殊的标记,来扩展功能。 例如,你创建了一个 'Special users' 组,并且允许组中的用户访问站点的一些VIP部分,或者发送VIP的邮件消息。
和用户管理一样,admin接口是管理组的最简单的方法。 然而,组也就是Django模型 django.contrib.auth.models ,因此你可以使用Django的数据库API,在底层访问这些组。
消息
消息系统会为给定的用户接收消息。 每个消息都和一个 User 相关联。
在每个成功的操作以后,Django的admin管理接口就会使用消息机制。 例如,当你创建了一个对象,你会在admin页面的顶上看到 The object was created successfully 的消息。
你也可以使用相同的API在你自己的应用中排队接收和显示消息。 API非常地简单:
-
要创建一条新的消息,user.message_set.create(message='message_text') 。
-
要获得/删除消息,使用 user.get_and_delete_messages() ,这会返回一个 Message 对象的列表,并且从队列中删除返回的项。
例子视图中,系统在创建了播放单(playlist)以后,为用户保存了一条消息。
def create_playlist(request, songs):
# Create the playlist with the given songs.
# ...
request.user.message_set.create(
message="Your playlist was added successfully."
)
return render_to_response("playlists/create.html",
context_instance=RequestContext(request))
当使用 RequestContext ,当前登录的用户以及他(她)的消息,就会以模板变量 {{ messages }} 出现在模板`的context中。
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
需要注意的是 RequestContext 会在后台调用 get_and_delete_messages ,因此即使你没有显示它们,它们也会被删除掉。这个消息框架只能服务于在用户数据库中存在的用户。 如果要向匿名用户发送消息,请直接使用会话框架。