Django框架-cookie和session以及中间件

一、cookie 和 session

1.为什么会有这些技术

cookie和session的出现是为了保存客户端的用户状态。

究其本质,其实是因为HTTP协议是无状态的,没办法保存客户的登录信息及状态,因此需要用cookie和session来保存用户的状态。

cookie

cookie是客户端用来记录用户信息的一种容器,它以键值对的形式储存,比方说{'username':'st'}

当cookie发送到服务端后,服务端可以根据cookie携带的信息识别用户的身份。可以知道到底是张三在访问服务器还是李四在访问服务器。

这样就导致某些功能在被用户使用时,必须先登录,让服务端知道用户身份后才能使用。比如说淘宝的购物车,每个人都不一样,你只有登陆了,才能访问自己的购物车。

这就导致用户每次访问都要登录,显得很麻烦。如果可以登陆一次就可以记录状态,那就很方便了。

cookie应运而生。

用户登录后,登录信息保存在浏览器cookie中,只需登录一次输入信息,下一次再访问,浏览器就会自动帮你发送cookie。

服务端得知了用户信息,你也不用手动输入多次了,万事大吉,美滋滋!

session

万事无绝对。

cookie记录的用户信息往往包含了一些隐私信息。比如密码之类的敏感信息。

当cookie保存在浏览器时,cookie中的信息就会遭受各种意外泄露的风险。

一旦泄露,有可能会酿成大祸。

为了解决这个问题,session横空出世。

当用户输入信息登录时,用户信息被传入后端服务器,服务器将这些数据进行加密并保存在服务器中,然后给这份信息贴上一个名为"session_id"的标签。这个"session_id"其实只是一个随机生成的字符串,用来标记这份用户信息,方便查找的。

通过这个"session_id",服务器就可以快速的访问到这份用户信息。

这样有什么用呢?

这样浏览器就不用直接保存用户信息了。服务端可以将"session_id"返回给浏览器,浏览器将"session_id"当做cookie键值对中的值进行保存。

这样,下次用户想要进行访问时,浏览器就将包含"session_id"的cookie发给服务器,服务器通过"session_id"找到之前用户第一次登陆时保存在服务器上的信息,服务器就可以知道用户身份惹!

至此登陆成功!用户再也不用频繁输入信息,也不怕信息泄露惹!

当然,session也并不完美,但是安全性能相比于只有cookie时已经大大提高,从而被广而告之,程序员们纷纷效仿。。。

虽然cookie可以保存在客户端浏览器上,但是由于它是由服务端设置出来的,浏览器有权禁止cookie的写入。

2.1 Django如何设置cookie

需要用views三基类实例化出的obj对象进行cookie设置。

obj = HttpResponse()
# 利用obj对象你才可以操作cookie
# obj = render()
# obj = redirect()

obj.set_cookie('k1','v1')
# 设置用盐加密的cookie
obj.set_signed_cookie(key,value,salt='加密盐',)

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'st' and password == '123':
            # 如果登录信息完整,就返回主页
            obj = redirect('/home/')
            
		   # 告诉浏览器,保存这一对cookie
            obj.set_cookie('username','st')
            
            return obj
    return render(request,'login.html')

2.2 Django如何获取cookie

def home(request):
    if request.COOKIES.get('username'):
        print(request.COOKIES.get('username'))
        # st
        return HttpResponse('只有登录的人才能看到')
    return redirect('/login/')

2.3 Django如何设置cookie的超时时间

set_cookies使用max_age、expires参数

obj.set_cookie('k1','v1',max_age=3)
obj.set_cookie('k1','v1',expires=3)
# 两个参数都是设置超时时间  并且都是以秒为单位
# 区别:expires可以给IE浏览器设置cookie的超时时间

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'st' and password == '123':
            obj = redirect('/home/')
            obj.set_cookie('username','st',expires=3)
            # 告诉浏览器,保存这一对cookie
            # expires=3单位为秒,max_age一样
            return obj
    return render(request,'login.html')

2.4 Django如何通过cookie跳转到想要访问的页面

可以在登录认证装饰器中完成

# 用来跳回用户登录前想访问的页面的装饰器
from functools import wraps
def login_auth(func):
    @wraps(func)  # 函数用wraps,类用update_wrapper
    def inner(*args,**kwargs):
        # 判断当前用户是否登录
        request = args[0]
        # print(request)
        # print(request.GET)
        # print(request.path_info)  # 只获取url,不获取get参数
        # print(request.get_full_path())  # # url+get参数
        if request.COOKIES.get('username'):
            res = func(*args, **kwargs)
            return res
        else:
            # 获取到用户想要访问的页面的url
            path = request.path_info
            # 将该url当做get参数传入login函数
            return redirect(f'/login/?path={path}')
    return inner


# login
def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'st' and password == '123':
            # 获取get参数,取得用户想要访问的url
            path = request.GET.get('path')
            if path:
                obj = redirect(path)
            else:
                # 如果path没值,可能用户是直接访问的login视图,因此登录后默认返回到home首页
                obj = redirect('/home/')

            obj.set_cookie('username','st')
            return obj
    return render(request,'login.html')

2.5 Django如何删除cookie

delete_cookie函数删除cookie。

def logout(request):
    obj = redirect('/logout/')
    obj.delete_cookie('username')
    return obj

3. session

session是保存在服务端上的键值对。

session的工作机制是需要依赖于cookie的。

3.1 Django设置session

默认失效时间为两周,14天

def set_session(request):
    request.session['k1'] = 'ok123'
    return HttpResponse('session已设置')

Django内部到底发生了什么:

  1. 将获取到的数据以键值对的形式添加到一个大字典中,将这个字典加密保存。
  2. 自动生成一个随机的字符串,作为上一步获取的加密数据的session_id。以键值对{'session_id':加密数据}的形式保存在储存器中。可以通过session_id来获取对应的加密数据。
  3. 将session_id返回给前端浏览器。这样下次浏览器访问时携带cookie,服务器后端可以获取cookie中的session_id,这样就可以通过session_id获取储存器中储存的加密数据。然后通过‘加密数据加密时使用的加密算法’所对应的解密算法将加密数据解密,就可以获取以键值对形式储存的数据了。
  4. 当下一次同一个浏览器需要在储存一组隐私数据时,django自动解密出该浏览器所对应的大字典,将新加入的键值对添加到这个大字典中,重新加密,保存,替代之前的加密大字典,这样,一个浏览器就会只有一条数据了。

3.2 Django获取session

# 获取session
def get_session(request):
    res = request.session.get('k1')
    print(res)
    return HttpResponse('获取成功')

Django内部到底发生了什么:

  1. Django服务端从前端浏览器处获取到cookie
  2. 从cookie中取出相应的'session_id'值
  3. 在数据库中通过session_id获取对应的大字典加密值
  4. 将加密大字典解密,获取大字典
  5. request.session=大字典
  6. 然后request.session就可以通过'.get'的方式从字典中取值了。
res = dict(request.session)
print(res)

# 强制类型转换一下,发现确实是一个字典
# {'k1': 'ok123', 'k2': 'ok321'}

3.3 Django删除session

# 删除session
def del_session(request):
    
    # 删除当前会话的所有Session数据
    # request.session.delete()
    
    # 删除当前的会话数据并删除会话的Cookie,
    # 用于确保前面的会话数据不可以再次被用户的浏览器访问.建议使用
    request.session.flush()
    return HttpResponse('shanle ')

3.4 Django设置session失效时间

# request.session.set_expiry(value)
request.session.set_expiry(5)  # 单位为秒

当某条session失效后,这个数据就会从有效的大字典中被筛选出,单独放在session表里作为一条数据,它不会立马被清除,而是先存在session表里,每过一段时间,django就会启用自动清除功能,将失效时间已过的session删除。

3.5 Django设置储存session的存储器

1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用设置项:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)

Django中Session相关设置

二、Django中间件

1. 默认中间件

如果你想要做一些网站的全局性功能,你都应该优先考虑使用django的中间件。

例如:

1.全局的用户登录校验
2.全局的用户访问频率校验
3.全局的用户权限校验()

django的中间件是所有框架里面做的最完善的。

# settings
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

以上就是django默认的七种中间件。

中间件的本质其实就是一个个的类,类中的以下方法会在特定的阶段自动触发:

1.process_request

2.process_response

3.process_view

4.process_template_response

5.process_exception

2. 自定义中间件

自定义中间件需要自己创建一个py文件,名字可以随便取,然后在这个文件中导入MiddlewareMixin模块产生子类。

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,render,redirect
# 接下来可以定义类,继承MiddlewareMixin模块,该类就成为了一个中间件

class MyMdd1(MiddlewareMixin):
    def process_request(self,request):
        pass
    
# 。。。。。。

2.1. 需要掌握

  1. process_request

    1.请求来的时候会按照settings配置文件中从上往下的顺序,依次执行每一个中间件内部定义的process_request方法。

    2.如果中间件内部没有该方法,直接跳过执行下一个中间件。

    3.该方法一旦返回了HttpResponse对象,那么请求会立刻停止往回走,立即原路返回。

    4.当process_request方法直接返回HttpResponse对象之后 会直接从当前中间件里面的process_respone往回走。原路返回。没有执行的中间件都不会再执行。

def process_request(self,request):
    print('我是第一个中间件里面的process_request方法')
    # return HttpResponse("我是中间件一里面的")
  1. process_response

    1.响应走的时候会按照settings配置文件中从下往上的顺序,依次执行每一个中间件内部定义的process_response方法。

    2.该方法必须有两个形参,并且必须返回response形参,不返回直接报错。

    3.该方法返回什么(HttpResponsed对象)前端就能获得什么。

def process_response(self,request,response):
	"""
	:param request:
	:param response: 就是后端返回给前端的数据
	:return:
	"""
	print('我是第一个中间件里面的process_response方法')
	return response

2.2 了解

  1. process_view

    1.路由匹配成功之后执行视图函数之前触发。

    2.如果该方法返回了HttpResponse对象,那么会从下往上一次经过每一个中间件里面的process_response方法。

def process_view(self,request,view_name,*args,**kwargs):
    print(view_name)  # 函数内存地址
    print(args)
    print(kwargs)
    print('我是第一个中间件里面的process_view方法')
  1. process_template_response

    1.在视图函数执行完成后立即执行,但有一个前提条件,那就是视图函数返回的对象必须有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。不然不会触发。

def process_template_response(self, request, response):
    print('我是第一个中间件里面的奇葩方法')
    return response


# views
def mdzz(request):
    print('我是视图函数mdzz')
    def render():
        return HttpResponse('你好呀 我是奇葩')
    obj = HttpResponse('我很好 好的像个傻逼一样')
    obj.render = render
    return obj
  1. process_exception

    1.当视图函数中出现错误时会自动触发,顺序是从下往上

def process_exception(self,request,exception):
    print('我是第一个中间件里面的process_exception')
    print(exception)
posted @ 2019-12-04 23:31  Donner  阅读(176)  评论(0编辑  收藏  举报