Django学习18-Session会话


Django完全支持也匿名会话,简单说就是使用跨网页之间可以进行通讯,比如显示用户名,用户是否已经发表评论。session框架让你存储和获取访问者的数据信息,这些信息保存在服务器上(默认是数据库中),以 cookies 的方式发送和获取一个包含 session ID的值,并不是用cookies传递数据本身(除非使用了基于cookie的会话)。

启用session

想要启动Django的会话功能需要:

  • 确保在MIDDLEWARE中添加了'django.contrib.sessions.middleware.SessionMiddleware'
  • INSTALLED_APPS添加'django.contrib.sessions'应用

默认创建的项目是包含这会话服务的,如果确定不需要使用会话功能,也可以移除这两项,为服务器减少开支。

配置session

默认情况下,Django会将会话存储在数据库中(使用模型django.contrib.sessions.models.Session)虽然这很方便,但在某些设置中,将会话数据存储在其他位置的速度更快,因此可以将Django配置为在文件系统或缓存中存储会话数据。

使用数据库存储会话

这是默认设置,只要在INSTALLED_APPS添加'django.contrib.sessions'就可以。
执行manage.py migrate命令时,就会在数据库创建会话表:
在这里插入图片描述

使用Cache存储会话

使用基于缓存后端的会话能获取更好的性能。首先确保项目设置好了缓存,缓存配置
如果在CACHES定义了多种缓存,Django会使用默认的缓存存储会话。可以用
SESSION_CACHE_ALIAS指定使用的缓存后端。缓存配置完成后,有2中方式使用缓存存储会话:

  1. 简单直接地将SESSION_ENGINE设置为'django.contrib.sessions.backends.cache',这样会话会直接存储在缓存中。但是,会话数据可能不是持久性的:如果缓存填满或缓存服务器重新启动,缓存数据就会被逐出。
  2. 对于要持久性的缓存会话数据,将SESSION_ENGIN设置为'django.contrib.sessions.backends.cached_db',这使用了直写高速缓存:每次写入高速缓存的数据也将会写入数据库。如果数据已经不在缓存中,则只会用数据库读取会话。

两个会话存储都非常快,但使用简单的缓存更快,因为它忽略了持久性。在大多数情况下,cached_db后端速度足够快。但如果需要最后一点性能,并且愿意让会话数据不时被清除,则简单的缓存后端更适合。
如果使用cached_db会话后端,则还需要遵循使用数据库支持的会话的配置说明。

Warning

只有在使用基于Memcached的缓存后端时,才能使用缓存存储会话。 数据在本地内存缓存后端保存的时间不够长,并且直接使用文件数据库会话比通过文件或数据库缓存后端发送所有内容会更快。 此外,本地内存缓存后端不是多进程安全的,因此可能不适合生产环境。

使用文件存储会话

使用基于文件的会话,需要将SESSION_ENIGN设置为'django.contrib.sessions.backends.file'。还可以设置SESSION_FILE_PATH,来控制Django存储会话文件的位置,需要检查Web服务器是否有读取和写入该文件的权限。

使用cookie存储会话

使用基于cookie的会话,需要设置SESSION_ENIGN设置为'django.contrib.sessions.backends.signed_cookies。会话数据将使用Django的加密签名工具和SECRET_KEY设置进行存储。同时建议设置SESSION_COOKIE_HTTPONLY为True,防止JS脚本访问存储的数据。
注意:如果SECRET_KEY没有保密并且正在使用PickleSerializer,则可能导致任意远程代码执行。

在视图函数中启用session

SessionMiddleware被激活时, 每个HttpRequest(每个视图函数的第一个参数)都会有一个类似python 字典表的session属性。request.session可以在视图中任何地方使用。
默认情况下,Django使用JSON格式序列化session。可以用SESSION_SERIALIZER选择其他方式或是自定义的序列化方式,但强烈推荐使用默认的JSON方式。

# 创建或修改 session:
request.session[key] = value
# 获取 session:
request.session.get(key,default=None)
# 删除 session
del request.session[key] # 不存在时报错

session的属性和方法可以查看其基类

session的使用指南:

  • 在request.session上使用普通的Python字符串作为字典键。 这更像是一种惯例而非硬性规则。
  • 以下划线开头的会话字典键保留供Django内部使用。
  • 不要使用新对象覆盖request.session,也不要访问或设置其属性。 像Python字典一样使用它。

例如使用session来完成最简单的登录、退出功能:

def login(request):
    m = Member.objects.get(username=request.POST['username'])
    if m.password == request.POST['password']:
        request.session['member_id'] = m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")
        
def logout(request):
    try:
        del request.session['member_id']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

而事实上标准的用户退出函数django.contrib.auth.logout()也会调用request.session的flush方法来清除会话中的数据和会话cookie。

测试cookie

Django 提供了一种非常简单方法来测试用户使用的浏览器是否支持使用cookie,
只要在在视图中调用request.sessionset_test_cookie方法, 再在随后的视图(不是在同一次请求中)中调用test_cookie_worked方法。

from django.http import HttpResponse
from django.shortcuts import render

def login(request):
    if request.method == 'POST':
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, 'foo/login_form.html')

当确认浏览器可以使用cookie后,删除这个测试用的cookie。

在视图函数外使用session

可以调用API来处理视图外的session:

>>> from django.conf import settings
>>> SessionStore=import_module(settings.SESSION_ENGINE).SessionStore
>>> s =  SessionStore()
>>> s.session_key
>>> s['last_login'] = 123456
>>> s.create()
>>> s.session_key
'wcg2thkiwynu2b2ji013x340x61iaf2n'
>>> s_new = SessionStore(session_key='wcg2thkiwynu2b2ji013x340x61iaf2n')
>>> s_new['last_login']
123456
>>> 


默认是使用数据库存储会话的,如果使用了其他方式,那么调用SessionStore的方式需要根据SESSION_ENGINE设置来。
SessionStore.create()会创建一个新的会话,此前session_key还是None,它会调用.save()方法,而.save()方法也会调用.create()方法,它们循环互相调用,直到生成session_key.

    def create(self):
        while True:
            self._session_key = self._get_new_session_key()
            try:
                # Save immediately to ensure we have a unique entry in the
                # database.
                self.save(must_create=True)
# ...

    def save(self, must_create=False):
        """
        Save the current session data to the database. If 'must_create' is
        True, raise a database error if the saving operation doesn't create a
        new entry (as opposed to possibly updating an existing entry).
        """
        if self.session_key is None:
            return self.create()
        data = self._get_session(no_load=must_create)

当使用数据库存储会话的时,Session模型就存储在django/contrib/sessions/models.py 中,而且完全可以用数据库API将它当成普通的数据库模型对象来访问:

>>> s = Session.objects.get(pk='vs10cbje57bcfmbv44603jxgnujp9mg1')
>>> s.expire_date
datetime.datetime(2018, 11, 23, 12, 51, 24, 365464, tzinfo=<UTC>)
>>> s.session_data
'YmVmNGFmZmFhN2M2YzgxNWUwNTAyYTliZT...'
>>> s.get_decoded()
{'_language': 'en', '_auth_user_id': '1', '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend', ...}
>>> 

使用get_decoded 获取解码后的数据。

会话何时进行存储

默认设置下,Django会在会话内数据发生改变时存储会话,即指定数据或删除数据时。

# 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'

在上面示例的最后一种情况中,我们可以通过在会话对象上设置modified属性来明确告诉会话对象它已被修改:request.session.modified = True。若不想使用这种方法,可以在settings中设置SESSION_SAVE_EVERY_REQUEST,但注意这样设置后会在每次请求时都发生会话cookie(原本只在会话创建或修改时发送),而且过期时间也会随着会话的每次发送而更新。
当响应状态码为500 时,会话不会被保存。

会话保持时间

通过设置settings下的SESSION_EXPIRE_AT_BROWSER_CLOSE参数,可以设置会话框架保存会话的时长,选择持续保存或是在维持直到浏览器关闭。默认设置是False,即会话会在浏览器中持续保存直到到达SESSION_COOKIE_AGE指定的时间。这项设置是全局的默认能保持时间,可以使用在视图中使用.set_expiry(value)覆盖。
某些浏览器(例如Chrome)提供的设置允许用户在关闭并重新打开浏览器后继续浏览会话。 在某些情况下,这可能会干扰SESSION_EXPIRE_AT_BROWSER_CLOSE设置,并阻止会话在浏览器关闭时到期。 在测试启用了SESSION_EXPIRE_AT_BROWSER_CLOSE设置的Django应用程序时请注意这一点

清除保存的会话

当用户在网站上创建新会话时,会话数据可能会累积在的会话存储中。如果正在使用数据库后端存储会话,则django_session数据库表将增长。如果正在使用文件后端,那么临时目录下将包含越来越多的文件。
使用数据库作为会话后端时,当用户登录时,Django会在django_session数据库表中添加一行。每次会话数据更改时,Django都会更新此行。如果用户手动注销,Django将删除该行。但是如果用户没有退出,则该行永远不会被删除。文件后端也会发生类似的过程。
Django不会自动清除过期的会话的。因此,您的工作是定期清除过期的会话。 Django为此提供了一个清理管理命令:clearsessions,建议定期调用此命令,它会清除那些超过有效期限的会话。注意,缓存后端不容易受到此问题的影响,因为缓存会自动删除过时数据。 Cookie后端也不是,因为会话数据是由用户的浏览器存储的。

会话相关的设置

在settings中与会话相关的设置,包括上文提到的:

  • SESSION_CACHE_ALIAS:选择使用哪种缓存存储会话(使用缓存作为会话后端);
  • SESSION_COOKIE_AGE:会话保存时间,默认1209600秒(2周);
  • SESSION_COOKIE_DOMAIN:允许使用会话的域名,将此设置为字符串,例如example.com用于跨域cookie,或者None为对标准域使用cookie。
  • SESSION_COOKIE_HTTPONLY:默认为True,客户端的js脚本无法访问会话cookie。
  • SESSION_COOKIE_NAME:会话cookie的名字,默认为sessionid,可以随意设置,只要能与其他cookie区分;
  • SESSION_COOKIE_PATH:会话cookie上设置的路径。 这应该与Django安装的URL路径匹配,或者是该路径的父路径。如果您在同一主机名下运行多个Django实例,这将非常有用。 他们可以使用不同的cookie路径,每个实例只能看到自己的会话cookie。
  • SESSION_COOKIE_SAMESITE:会话cookie上的SameSite标志的值。 此标志可防止cookie在跨站点请求中发送,从而防止CSRF攻击并使某些方法无法窃取会话cookie,默认为'Lax'详情
  • SESSION_COOKIE_SECURE:是否为会话cookie使用安全cookie,默认False,不使用。 如果将其设置为True,则cookie将被标记为“安全”,这意味着浏览器可以确保cookie仅在HTTPS连接下发送。保留此设置并不是一个好主意,因为攻击者可以使用数据包嗅探器捕获未加密的会话cookie,并使用cookie来劫持用户的会话。
  • SESSION_ENGINE:设置会话后端,可设置
    'django.contrib.sessions.backends.db',默认
    'django.contrib.sessions.backends.file'
    'django.contrib.sessions.backends.cache'
    'django.contrib.sessions.backends.cached_db'
    'django.contrib.sessions.backends.signed_cookies'
  • SESSION_EXPIRE_AT_BROWSER_CLOSE:默认False,设置浏览器关闭时会话是否马上过期。
  • SESSION_FILE_PATH: 使用文件后端存储会话,设置文件的位置,默认(None)
  • SESSION_SAVE_EVERY_REQUEST:是否每次请求时都保存会话,默认False;
  • SESSION_SERIALIZER:选择其他方式或是自定义的序列化方式,默认JSON;

会话安全

站点内的子域可以在客户端上为整个域设置cookie。 如果允许来自不受可信用户控制的子域的cookie,则可以进行会话固定。
例如,攻击者可以登录good.example.com并获取其帐户的有效会话。 如果攻击者可以控制bad.example.com,他们可以使用它向您发送会话密钥,因为允许子域在* .example.com上设置cookie。 当您访问good.example.com时,您将以攻击者身份登录,并可能无意中将您的敏感个人数据(例如信用卡信息)输入攻击者帐户。
另一种可能的攻击方式是,如果good.example.com将其SESSION_COOKIE_DOMAIN设置为example.com,这会导致该站点的会话cookie被发送到 bad.example.com

猜你喜欢

转载自blog.csdn.net/qq_19268039/article/details/83956577
今日推荐