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 的,有缓存。

用户的身份验证过程:

  1. 首先通过 authenticate() 方法对传入的用户名、密码等信息进行验证,如果符合,则返回相应的 user 对象,同时,该方法会对 user 对象加以标注,通过附加 user.backend 属性来记录验证是被哪个配置的 backend 通过的。默认只有一个 backend,是 django.contrib.auth.backends.ModelBackend.

  2. 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视图函数。

--------EOF---------
微信分享/微信扫码阅读