Django中的session
1、Cookie
在说Session之前,一定要说一下Cookie。
浏览器的开发者在很早的时候就已经意识到, HTTP’s 的无状态会对Web开发者带来很大的问题,于是(cookies)应运而生。 cookies 是浏览器为 Web 服务器存储的一小段信息。 每次浏览器从某个服务器请求页面时,它向服务器回送之前收到的cookies。用cookie可能标识一个之前访问的客户。
Cookie是保存在客户端的,这也是它和Session比较大的不同之处。在Django中也可以设置cookie。这里就不多介绍了,可以参考官方文档。虽然cookie很方便,但是Cookie(特别是那些没通过HTTPS传输的)是非常不安全的。因为HTTP数据是以明文发送的,所以特别容易受到嗅探攻击。也就是说,嗅探攻击者可以在网络中拦截并读取cookies,因此你要绝对避免在cookies中存储敏感信息。这就意味着您不应该使用cookie来在存储任何敏感信息。
2、Django的Session介绍
Session是存储在服务器端的,但是它也是基于Cookie的,因为它要将session_id存储在Cookie中,服务器通过cookie中的session_id,获取session数据。session默认周期是2周。 在django中session有四种实现方式:
1、数据库;
2、缓存;
3、文件;
4、cookie
会话数据默认存储在数据中名为django_session 的表中, Django 只发送它需要的Cookie。如果你没有设置任何会话数据,它将不会发送会话Cookie。session_key就是cookie中的sessionid。
session的来源:当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该session对象,当浏览器下次(session继续有效时,一般都会设置过期时间)请求别的资源的时候,浏览器会偷偷地将sessionID放置到请求头中,服务器接收到请求后就得到该请求的sessionID,服务器找到该id的session返还给请求者使用。
看例子:
Set-Cookie:csrftoken=bLmE8r1sV5tnPHxhxH1Sk8MG; expires=Thu, 06-Dec-2018 10:09:48 GMT; Max-Age=31449600; Path=/
Set-Cookie:sessionid=bofezl1pjwwmumcn; expires=Thu, 21-Dec-2017 10:09:48 GMT; httponly; Max-Age=1209600; Path=/
这是第一次发出登录请求的时候,服务器的响应头。
当浏览器再次访问的时候,就会自动在请求头中带上:
Cookie:sessionid=bofe61xucdwzl1wwmumhwhcn; csrftoken=bLmE8r1wdvvxnPHxhxH1Sk8MG
说明:为了安全我改了,响应和请求的是同一个sessionid.别试着想用我的cookie去恶意弄我的网站啊。
一个会话只能有一个session对象,对session来说是只认id不认人。 session包括session_key,session_data,expire_date等属性。session_key对应在cookie中保存的sessionid。用户信息默认存储到session中。保存的数据是经过Json序列化的数据。
说下用户认证和session的关系:
request.user 中保存的 user 对象,是 lazy-load 的,有缓存。
用户的身份验证过程:
-
首先通过 authenticate() 方法对传入的用户名、密码等信息进行验证,如果符合,则返回相应的 user 对象,同时,该方法会对 user 对象加以标注,通过附加 user.backend 属性来记录验证是被哪个配置的 backend 通过的。默认只有一个 backend,是 django.contrib.auth.backends.ModelBackend.
-
login 方法调用
如果上一步身份验证通过,则此方法中对 request.session 中简单的添加两个键值:
(1) "_auth_user_id" 这个是 user.id
(2) "_auth_user_backend" 这个是 user.backend
而实现 request.user 属性同样是通过 Middleware 来完成的,其中调用到一个 get_user 方法,该方法尝试读取上面在 session 中记录的 user.id 和 user.backend, 然后命令 backend 去查找相关 id 的 user 对象。如果没有找到,则返回一个 AnonymousUser. 而 AnonymousUser 是一个空实现,不具备 user 的任何功能。
来看看这段实现的源代码。有一个中间件叫AuthenticationMiddleware,它在每次请求处理之前都会调用:
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE_CLASSES setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
)
request.user = SimpleLazyObject(lambda: get_user(request))
看到了吧,request.user.再看get_user方法:
def get_user(request):
"""
Returns the user model instance associated with the given request session.
If no user is retrieved an instance of `AnonymousUser` is returned.
"""
from .models import AnonymousUser
user = None
try:
user_id = _get_user_session_key(request)
backend_path = request.session[BACKEND_SESSION_KEY]
except KeyError:
pass
else:
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id)
# Verify the session
if ('django.contrib.auth.middleware.SessionAuthenticationMiddleware'
in settings.MIDDLEWARE_CLASSES and hasattr(user, 'get_session_auth_hash')):
session_hash = request.session.get(HASH_SESSION_KEY)
session_hash_verified = session_hash and constant_time_compare(
session_hash,
user.get_session_auth_hash()
)
if not session_hash_verified:
request.session.flush()
user = None
return user or AnonymousUser()
get_user方法,会首先检查session中是否保存了当前用户对应的user_id,如果有,则通过user_id在model查找相应的用户返回,否则返回一个匿名用户(AnonymousUser)。
接下来,我们就可以通过 is_anonymous() 或 is_authenticated() 来判别是否为匿名用户。请放心,Django会为每一个客户端都提供会话功能。
Django中session的使用
配置session:
settings.py
INSTALLED_APPS = [
'django.contrib.sessions',
]
MIDDLEWARE_CLASSES = [
#middleware, a framework of hooks into Django’s request/response processing
'django.contrib.sessions.middleware.SessionMiddleware',
]
django.contrib.sessions配置之后,session数据会保存在数据表django_sessions中。
session可以像Python的字典一样,进行操作。但是有一点要注意的就是只能用字符串作为Key。
在视图中使用: 每一个request都有一个session属性。
def set_session(request):
request.session['haibo'] = 'lina'
return HttpResponse('set session success')
def get_session(request):
session_value = request.session.get('haibo')
if session_value:
return HttpResponse('the session is %s' % session_value)
else:
return HttpResponse('no session data')
通过F12开发工具测试,会看到请求cookie和响应cookie。
就如上面所说,session是基于cookie的,因此使用session前,最好要检查一下cookie是否能够使用,很有可能浏览器禁用了cookie。
def get_session(request):
request.session.set_test_cookie()
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
sessionid = request.COOKIES['sessionid']
session_value = Session.objects.get(pk=sessionid)
if session_value:
return HttpResponse('the session is %s,%s,' % (sessionid,session_value.get_decoded()))
else:
return HttpResponse('no session data')
else:
return HttpResponse('cookie has been disabled,please open it first')
注意一点,Django是唯一基于cookie的,如果浏览器禁止了Cookie,就不能用了,不能像PHP一样将url后带上参数sessionid。
默认情况下,只有在session发生变化的情况下,才保存到数据库,如果SESSION_SAVE_EVERY_REQUEST设为True,就会在每次请求的时候,都会保存一次。
Session的实现大体流程是sessionmiddleware在每次请求时都会实例化一个Session(在process_request方法中实现),然后将请求返给view视图函数。
微信分享/微信扫码阅读