会话

存取Cookies

在Django中处理持久化,大部分时候你会更愿意用高层些的session 和/或 后面要讨论的user 框架。 但在此之前,我们需要停下来在底层看看如何读写cookies。 这会帮助你理解本章节后面要讨论的工具是如何工作的,而且如果你需要自己操作cookies,这也会有所帮助。

读取已经设置好的cookies极其简单。 每一个

HttpRequest

对象都有一个

COOKIES

对象,该对象的行为类似一个字典,你可以使用它读取任何浏览器发送给视图(view)的cookies。

def show_color(request):
    if favorite_color in request.COOKIES:
        return HttpResponse(Your favorite color is %s % request.COOKIES[favorite_color])
    else:
        return HttpResponse(You don't have a favorite color.)


写cookies稍微复杂点。 你需要使用 HttpResponse对象的 set_cookie()方法。 这儿有个基于 GET 参数来设置

def set_color(request):
    if favorite_color in request.GET:
        # Create an HttpResponse object...
        response = HttpResponse(Your favorite color is now %s % request.GET[favorite_color])
        # ... and set a cookie on the response
        response.set_cookie(favorite_color, request.GET[favorite_color])
        return response
    else:
        return HttpResponse(You didn't give a favorite color.)


你可以给 response.set_cookie() 传递一些可选的参数来控制cookie的行为。

Django的 Session 框架

由于存在的限制与安全漏洞,cookies和持续性会话已经成为Web开发中令人头疼的典范。 好消息是,Django的目标正是高效的"头疼杀手",它自带的session框架会帮你搞定这些问题。

你可以用session 框架来存取每个访问者任意数据, 这些数据在服务器端存储,并对cookie的收发进行了抽象。 Cookies只存储数据的哈希会话ID,而不是数据本身,从而避免了大部分的常见cookie问题。

打开 Sessions功能

Sessions 功能是通过一个中间件和一个模型(model)来实现的。 要打开sessions功能,需要以下几步操作:

如果项目是用 startproject 来创建的,配置文件中都已经安装了这些东西,除非你自己删除,正常情况下,你无需任何设置就可以使用session功能。

如果不需要session功能,你可以删除 MIDDLEWARE_CLASSES 设置中的 SessionMiddleware 和 INSTALLED_APPS 设置中的 'django.contrib.sessions' 。虽然这只会节省很少的开销,但积少成多啊。

在视图中使用Session

SessionMiddleware 激活后,每个传给视图(view)函数的第一个参数

HttpRequest

对象都有一个 session 属性,这是一个字典型的对象。 你可以象用普通字典一样来用它。 例如,在视图(view)中你可以这样用:

# Set a session value:
request.session[fav_color] = blue

# Get a session value -- this could be called in a different view,
# or many requests later (or both):
fav_color = request.session[fav_color]

# Clear an item from the session:
del request.session[fav_color]

# Check if the session has a given key:
if fav_color in request.session:


设置测试Cookies

就像前面提到的,你不能指望所有的浏览器都可以接受cookie。 因此,为了使用方便,Django提供了一个简单的方法来测试用户的浏览器是否接受cookie。 你只需在视图(view)中调用 request.session.set_test_cookie(),并在后续的视图(view)、而不是当前的视图(view)中检查 request.session.test_cookie_worked() 。

用户与Authentication

通过session,我们可以在多次浏览器请求中保持数据, 接下来的部分就是用session来处理用户登录了。 当然,不能仅凭用户的一面之词,我们就相信,所以我们需要认证。

当然了,Django 也提供了工具来处理这样的常见任务(就像其他常见任务一样)。 Django 用户认证系统处理用户帐号,组,权限以及基于cookie的用户会话。 这个系统一般被称为 auth/auth (认证与授权)系统。 这个系统的名称同时也表明了用户常见的两步处理。 我们需要

根据这些需求,Django 认证/授权 系统会包含以下的部分:

打开认证支持

像session工具一样,认证支持也是一个Django应用,放在 django.contrib 中,所以也需要安装。 与session系统相似,它也是缺省安装的,但如果它已经被删除了,通过以下步骤也能重新安装上:

  1. 根据本章早前的部分确认已经安装了session 框架。 需要确认用户使用cookie,这样sesson 框架才能正常使用。
  2. 将 'django.contrib.auth' 放在你的 INSTALLED_APPS 设置中,然后运行 manage.py syncdb以创建对应的数据库表。
  3. 确认 SessionMiddleware 后面的 MIDDLEWARE_CLASSES 设置中包含 'django.contrib.auth.middleware.AuthenticationMiddleware' SessionMiddleware。

登录和退出

Django 提供内置的视图(view)函数用于处理登录和退出 (以及其他奇技淫巧),但在开始前,我们来看看如何手工登录和退出。 Django提供两个函数来执行django.contrib.auth中的动作 : authenticate() 和login()。

认证给出的用户名和密码,使用 authenticate() 函数。它接受两个参数,用户名 username 和 密码 password ,并在密码对给出的用户名合法的情况下返回一个 User 对象。 如果密码不合法,authenticate()返回None。

>>> from django.contrib import auth
>>> user = auth.authenticate(username='john', password='secret')
>>> if user is not None:
...     print Correct!
... else:
...     print Invalid password.


authenticate() 只是验证一个用户的证书而已。 而要登录一个用户,使用 login() 。该函数接受一个 HttpRequest 对象和一个 User 对象作为参数并使用Django的会话( session )框架把用户的ID保存在该会话中。

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/)


注销一个用户,在你的视图中使用 django.contrib.auth.logout() 。 它接受一个HttpRequest对象并且没有返回值。

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),
)


限制已登录用户的访问

有很多原因需要控制用户访问站点的某部分。

一个简单原始的限制方法是检查 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 做下面的事情:

对通过测试的用户限制访问

限制访问可以基于某种权限,某些检查或者为login视图提供不同的位置,这些实现方式大致相同。

一般的方法是直接在视图的 request.user 上运行检查。 例如,下面视图确认用户登录并是否有 polls.can_vote权限:

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