会话 用户 和注册在线手册地址
http://docs.oneele.com/django/topics
COOKIES
第一次打开浏览器,访问页面
Response Headers:
Set-Cookie:csrftoken=369caefb109c7a58ed947660ce99104b; expires=Fri, 27-Feb-2015 08:17:35 GMT; Max-Age=31449600; Path=/
Set-Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; expires=Fri, 27-Feb-2015 08:20:40 GMT; Max-Age=31449600; Path=/
Set-Cookie:sessionid=942fc061ff46f73b998e3f3e06ce2eb7; expires=Fri, 14-Mar-2014 08:22:17 GMT; Max-Age=1209600; Path=/
接下来再次访问同一个页面
Request Headers:
Cookie:csrftoken=369caefb109c7a58ed947660ce99104b
Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; sessionid=61183c29da0f03d5dbbe55e8c696afdc
Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; sessionid=942fc061ff46f73b998e3f3e06ce2eb7
from django.contrib.sessions.models import Session
s = Session.objects.get(sessionid='942fc061ff46f73b998e3f3e06ce2eb7')
是时候承认了: 我们有意的避开了Web开发中极其重要的方面。 到目前为止,我们都在假定,网站流量是大
量的匿名用户带来的。
这当然不对。 浏览器的背后都是活生生的人(至少某些时候是)。 这忽略了重要的一点: 互联网服务于人而不是
机器。 要开发一个真正令人心动的网站,我们必须面对浏览器后面活生生的人。
很不幸,这并不容易。 HTTP被设计为”无状态”,每次请求都处于相同的空间中。 在一次请求和下一次请求
之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,用户代理等)来识别来自同一人的连续请求。
在本章中你将学会如何搞定状态的问题。 好了,我们会从较低的层次(cookies)开始,然后过渡到用高层的工
来搞定会话,用户和注册的问题。
Cookies
浏览器的开发者在很早的时候就已经意识到, HTTP’s 的无状态会对Web开发者带来很大的问题,于是
(cookies)应运而生。 cookies 是浏览器为 Web 服务器存储的一小段信息。 每次浏览器从某个服务器请求页
时,它向服务器回送之前收到的cookies
来看看它是怎么工作的。 当你打开浏览器并访问 google.com ,你的浏览器会给Google发送一个HTTP请求,
起始部分就象这样:
GET / HTTP/1.1
Host: google.com
...
当 Google响应时,HTTP的响应是这样的:
HTTP/1.1 200 OK
Content‐Type: text/html
Set‐Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
expires=Sun, 17‐Jan‐2038 19:14:07 GMT;
path=/; domain=.google.com
Server: GWS/2.1
Django的 Session 框架
由于存在的限制与安全漏洞,cookies和持续性会话已经成为Web开发中令人头疼的典范。 好消息是,Django
的目标正是高效的“头疼杀手”,它自带的session框架会帮你搞定这些问题。
你可以用session 框架来存取每个访问者任意数据, 这些数据在服务器端存储,并对cookie的收发进行了抽
象。 Cookies只存储数据的哈希会话ID,而不是数据本身,从而避免了大部分的常见cookie问题。
下面我们来看看如何打开session功能,并在视图中使用它。
1)打开 Sessions功能
Sessions 功能是通过一个中间件(参见第17章)和一个模型(model)来实现的。 要打开sessions功能,需要以下
几步操作:
1. 编辑 MIDDLEWARE_CLASSES 配置,确保 MIDDLEWARE_CLASSES 中包含
'django.contrib.sessions.middleware.SessionMiddleware'。
2. 确认 INSTALLED_APPS 中有 'django.contrib.sessions' (如果你是刚打开这个应用,别忘了运行
manage.py syncdb )
如果项目是用 startproject 来创建的,配置文件中都已经安装了这些东西,除非你自己删除,正常情况下,你
无需任何设置就可以使用session功能。
如果不需要session功能,你可以删除 MIDDLEWARE_CLASSES 设置中的 SessionMiddleware 和 INSTALLED_APPS 设
置中的 'django.contrib.sessions' 。虽然这只会节省很少的开销,但积少成多啊。
2)在视图中使用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:
其他的映射方法,如 keys() 和 items() 对 request.session 同样有效:
在视图(View)外使用Session
从内部来看,每个session都只是一个普通的Django model(在 django.contrib.sessions.models 中定义)。
每个session都由一个随机的32字节哈希串来标识,并存储于cookie中。 因为它是一个标准的模型,所以你可
以使用Django数据库API来存取session。
>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)
你需要使用get_decoded() 来读取实际的session数据。 这是必需的,因为字典存储为一种特定的编码格式。
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}
何时保存Session
缺省的情况下,Django只会在session发生变化的时候才会存入数据库,比如说,字典赋值或删除。
# Session is modified.
request.session['foo'] = 'bar'
# Session is modified.
del request.session['foo']
# Session is modified.
request.session['foo'] = {}
# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session['foo']['bar'] = 'baz'
你可以设置 SESSION_SAVE_EVERY_REQUEST 为 True 来改变这一缺省行为。
如果置为True的话,Django会在每收到请求的时候保存session,即使没发生变化。
注意,会话cookie只会在创建和修改的时候才会送出。 但如果 SESSION_SAVE_EVERY_REQUEST 设置为 True ,会
话cookie在每次请求的时候都会送出。 同时,每次会话cookie送出的时候,其 expires 参数都会更新。
在setting 中设置
SESSION_COOKIE_AGE=60*30 30分钟。
SESSION_EXPIRE_AT_BROWSER_CLOSE False:会话cookie可以在用户浏览器中保持有效期。True:关闭浏览器,则Cookie失效。
SESSION_COOKIE_DOMAIN 生效站点
SESSION_COOKIE_NAME cookie中保存session的名称
Session使用比较简单,在request.session是一个字典类。session是保存在数据库中的。
浏览器关闭即失效会话 vs 持久会话
你可能注意到了,Google给我们发送的cookie中有 expires=Sun, 17‐Jan‐2038 19:14:07 GMT; cookie可以有
过期时间,这样浏览器就知道什么时候可以删除cookie了。 如果cookie没有设置过期时间,当用户关闭浏览器
的时候,cookie就自动过期了。 你可以改变 SESSION_EXPIRE_AT_BROWSER_CLOSE 的设置来控制session框架的
这一行为。
缺省情况下, SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 False ,这样,会话cookie可以在用户浏览器中保持
有效达 SESSION_COOKIE_AGE 秒(缺省设置是两周,即1,209,600 秒)。 如果你不想用户每次打开浏览器都必须
重新登陆的话,用这个参数来帮你。
如果 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效。
其他的Session设置
除了上面提到的设置,还有一些其他的设置可以影响Django session框架如何使用cookie,详见表 14-2.
表 14-2. 影响cookie行为的设置 设置描述缺省
SESSION_COOKIE_DOMAIN使用会话cookie(session cookies)的站点。 将它设成一个 None
字符串,就好象`` “.example.com”`` 以用于跨站点(cross-domain)的cookie,或`` None`` 以用于单个站点。
SESSION_COOKIE_NAME会话中使用的cookie的名字。 它可以是任意的字符串。"sessionid"
SESSION_COOKIE_SECURE是否在session中使用安全cookie。 如果设置 True , cookie就 False
会标记为安全, 这意味着cookie只会通过HTTPS来传输。
技术细节
如果你还是好奇的话,下面是一些关于session框架内部工作方式的技术细节:
session 字典接受任何支持序列化的Python对象。 参考Python内建模块pickle的文档以获取更多信息。
Session 数据存在数据库表 django_session 中
Session 数据在需要的时候才会读取。 如果你从不使用 request.session , Django不会动相关数据库表的
一根毛。
Django 只在需要的时候才送出cookie。 如果你压根儿就没有设置任何会话数据,它不会 送出会话
cookie(除非 SESSION_SAVE_EVERY_REQUEST 设置为 True )。
Django session 框架完全而且只能基于cookie。 它不会后退到把会话ID编码在URL中(像某些工具
(PHP,JSP)那样)。
这是一个有意而为之的设计。 把session放在URL中不只是难看,更重要的是这让你的站点 很容易受到攻击
——通过 Referer header进行session ID”窃听”而实施的攻击。
如果你还是好奇,阅读源代码是最直接办法,详见 django.contrib.sessions 。
登录和退出
Django 提供内置的视图(view)函数用于处理登录和退出 (以及其他奇技淫巧),但在开始前,我们来看看如何
和退出。 Django提供两个函数来执行django.contrib.auth\中的动作 : authenticate()
和login()。
认证给出的用户名和密码,使用 authenticate() 函数。它接受两个参数,用户名 username 和 密码
password ,并在密码对给出的用户名合法的情况下返回一个 User 对象。 如果密码不合法,authenti
回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保存在
中。
下面的例子演示了如何在一个视图中同时使用 authenticate() 和 login() 函数:
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/")
注意,即使用户没有登录, logout() 也不会抛出任何异常。
http://docs.oneele.com/django/topics
COOKIES
第一次打开浏览器,访问页面
Response Headers:
Set-Cookie:csrftoken=369caefb109c7a58ed947660ce99104b; expires=Fri, 27-Feb-2015 08:17:35 GMT; Max-Age=31449600; Path=/
Set-Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; expires=Fri, 27-Feb-2015 08:20:40 GMT; Max-Age=31449600; Path=/
Set-Cookie:sessionid=942fc061ff46f73b998e3f3e06ce2eb7; expires=Fri, 14-Mar-2014 08:22:17 GMT; Max-Age=1209600; Path=/
接下来再次访问同一个页面
Request Headers:
Cookie:csrftoken=369caefb109c7a58ed947660ce99104b
Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; sessionid=61183c29da0f03d5dbbe55e8c696afdc
Cookie:csrftoken=8bc7a3911f266e87067ecf9dc71a26ec; sessionid=942fc061ff46f73b998e3f3e06ce2eb7
from django.contrib.sessions.models import Session
s = Session.objects.get(sessionid='942fc061ff46f73b998e3f3e06ce2eb7')
是时候承认了: 我们有意的避开了Web开发中极其重要的方面。 到目前为止,我们都在假定,网站流量是大
量的匿名用户带来的。
这当然不对。 浏览器的背后都是活生生的人(至少某些时候是)。 这忽略了重要的一点: 互联网服务于人而不是
机器。 要开发一个真正令人心动的网站,我们必须面对浏览器后面活生生的人。
很不幸,这并不容易。 HTTP被设计为”无状态”,每次请求都处于相同的空间中。 在一次请求和下一次请求
之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,用户代理等)来识别来自同一人的连续请求。
在本章中你将学会如何搞定状态的问题。 好了,我们会从较低的层次(cookies)开始,然后过渡到用高层的工
来搞定会话,用户和注册的问题。
Cookies
浏览器的开发者在很早的时候就已经意识到, HTTP’s 的无状态会对Web开发者带来很大的问题,于是
(cookies)应运而生。 cookies 是浏览器为 Web 服务器存储的一小段信息。 每次浏览器从某个服务器请求页
时,它向服务器回送之前收到的cookies
来看看它是怎么工作的。 当你打开浏览器并访问 google.com ,你的浏览器会给Google发送一个HTTP请求,
起始部分就象这样:
GET / HTTP/1.1
Host: google.com
...
当 Google响应时,HTTP的响应是这样的:
HTTP/1.1 200 OK
Content‐Type: text/html
Set‐Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671;
expires=Sun, 17‐Jan‐2038 19:14:07 GMT;
path=/; domain=.google.com
Server: GWS/2.1
Django的 Session 框架
由于存在的限制与安全漏洞,cookies和持续性会话已经成为Web开发中令人头疼的典范。 好消息是,Django
的目标正是高效的“头疼杀手”,它自带的session框架会帮你搞定这些问题。
你可以用session 框架来存取每个访问者任意数据, 这些数据在服务器端存储,并对cookie的收发进行了抽
象。 Cookies只存储数据的哈希会话ID,而不是数据本身,从而避免了大部分的常见cookie问题。
下面我们来看看如何打开session功能,并在视图中使用它。
1)打开 Sessions功能
Sessions 功能是通过一个中间件(参见第17章)和一个模型(model)来实现的。 要打开sessions功能,需要以下
几步操作:
1. 编辑 MIDDLEWARE_CLASSES 配置,确保 MIDDLEWARE_CLASSES 中包含
'django.contrib.sessions.middleware.SessionMiddleware'。
2. 确认 INSTALLED_APPS 中有 'django.contrib.sessions' (如果你是刚打开这个应用,别忘了运行
manage.py syncdb )
如果项目是用 startproject 来创建的,配置文件中都已经安装了这些东西,除非你自己删除,正常情况下,你
无需任何设置就可以使用session功能。
如果不需要session功能,你可以删除 MIDDLEWARE_CLASSES 设置中的 SessionMiddleware 和 INSTALLED_APPS 设
置中的 'django.contrib.sessions' 。虽然这只会节省很少的开销,但积少成多啊。
2)在视图中使用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:
其他的映射方法,如 keys() 和 items() 对 request.session 同样有效:
在视图(View)外使用Session
从内部来看,每个session都只是一个普通的Django model(在 django.contrib.sessions.models 中定义)。
每个session都由一个随机的32字节哈希串来标识,并存储于cookie中。 因为它是一个标准的模型,所以你可
以使用Django数据库API来存取session。
>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)
你需要使用get_decoded() 来读取实际的session数据。 这是必需的,因为字典存储为一种特定的编码格式。
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}
何时保存Session
缺省的情况下,Django只会在session发生变化的时候才会存入数据库,比如说,字典赋值或删除。
# Session is modified.
request.session['foo'] = 'bar'
# Session is modified.
del request.session['foo']
# Session is modified.
request.session['foo'] = {}
# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session['foo']['bar'] = 'baz'
你可以设置 SESSION_SAVE_EVERY_REQUEST 为 True 来改变这一缺省行为。
如果置为True的话,Django会在每收到请求的时候保存session,即使没发生变化。
注意,会话cookie只会在创建和修改的时候才会送出。 但如果 SESSION_SAVE_EVERY_REQUEST 设置为 True ,会
话cookie在每次请求的时候都会送出。 同时,每次会话cookie送出的时候,其 expires 参数都会更新。
在setting 中设置
SESSION_COOKIE_AGE=60*30 30分钟。
SESSION_EXPIRE_AT_BROWSER_CLOSE False:会话cookie可以在用户浏览器中保持有效期。True:关闭浏览器,则Cookie失效。
SESSION_COOKIE_DOMAIN 生效站点
SESSION_COOKIE_NAME cookie中保存session的名称
Session使用比较简单,在request.session是一个字典类。session是保存在数据库中的。
浏览器关闭即失效会话 vs 持久会话
你可能注意到了,Google给我们发送的cookie中有 expires=Sun, 17‐Jan‐2038 19:14:07 GMT; cookie可以有
过期时间,这样浏览器就知道什么时候可以删除cookie了。 如果cookie没有设置过期时间,当用户关闭浏览器
的时候,cookie就自动过期了。 你可以改变 SESSION_EXPIRE_AT_BROWSER_CLOSE 的设置来控制session框架的
这一行为。
缺省情况下, SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 False ,这样,会话cookie可以在用户浏览器中保持
有效达 SESSION_COOKIE_AGE 秒(缺省设置是两周,即1,209,600 秒)。 如果你不想用户每次打开浏览器都必须
重新登陆的话,用这个参数来帮你。
如果 SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效。
其他的Session设置
除了上面提到的设置,还有一些其他的设置可以影响Django session框架如何使用cookie,详见表 14-2.
表 14-2. 影响cookie行为的设置 设置描述缺省
SESSION_COOKIE_DOMAIN使用会话cookie(session cookies)的站点。 将它设成一个 None
字符串,就好象`` “.example.com”`` 以用于跨站点(cross-domain)的cookie,或`` None`` 以用于单个站点。
SESSION_COOKIE_NAME会话中使用的cookie的名字。 它可以是任意的字符串。"sessionid"
SESSION_COOKIE_SECURE是否在session中使用安全cookie。 如果设置 True , cookie就 False
会标记为安全, 这意味着cookie只会通过HTTPS来传输。
技术细节
如果你还是好奇的话,下面是一些关于session框架内部工作方式的技术细节:
session 字典接受任何支持序列化的Python对象。 参考Python内建模块pickle的文档以获取更多信息。
Session 数据存在数据库表 django_session 中
Session 数据在需要的时候才会读取。 如果你从不使用 request.session , Django不会动相关数据库表的
一根毛。
Django 只在需要的时候才送出cookie。 如果你压根儿就没有设置任何会话数据,它不会 送出会话
cookie(除非 SESSION_SAVE_EVERY_REQUEST 设置为 True )。
Django session 框架完全而且只能基于cookie。 它不会后退到把会话ID编码在URL中(像某些工具
(PHP,JSP)那样)。
这是一个有意而为之的设计。 把session放在URL中不只是难看,更重要的是这让你的站点 很容易受到攻击
——通过 Referer header进行session ID”窃听”而实施的攻击。
如果你还是好奇,阅读源代码是最直接办法,详见 django.contrib.sessions 。
登录和退出
Django 提供内置的视图(view)函数用于处理登录和退出 (以及其他奇技淫巧),但在开始前,我们来看看如何
和退出。 Django提供两个函数来执行django.contrib.auth\中的动作 : authenticate()
和login()。
认证给出的用户名和密码,使用 authenticate() 函数。它接受两个参数,用户名 username 和 密码
password ,并在密码对给出的用户名合法的情况下返回一个 User 对象。 如果密码不合法,authenti
回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保存在
中。
下面的例子演示了如何在一个视图中同时使用 authenticate() 和 login() 函数:
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/")
注意,即使用户没有登录, logout() 也不会抛出任何异常。