一、Cookie

(一)由来

HTTP协议的无状态特性导致每次的请求都是独立的,即客户端和服务器在某次会话中产生的数据不会被保存,因此产生了Cookie,用来保存客户端的用户状态

(二)什么是Cookie

  1. Cookie具体是指服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些信息,以便服务器通过Cookie提取有用信息从而判断访问者
  2. Cookie最大支持4096字节,保存在客户端,安全性低

(三)Django中操作Cookie

(1)设置Cookie

  1. 普通

    • obj.set_cookie(key,value,...)
    • obj为HttpResponse或render或redirect实例化的对象
  2. 加盐
    • obj.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
    • obj为HttpResponse或render或redirect实例化的对象
  • 参数

    1. key:键
    2. value:值
    3. max_age:超时时间,以秒为单位,不能给IE设置cookie
    4. expires:超时时间,以秒为单位,可以给IE设置cookie
    5. path:cookie生效的路径,/表示根路径,可以被任何URL页面访问
    6. domain:cookie生效的域名
    7. secure:布尔值,是否为HTTPS传输
    8. httponly:布尔值,只能http协议传输,无法被JavaScript获取
# views.py
def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

        if username == 'wick' and password == '123':
            next_url = request.GET.get('next')
            obj = redirect('/home/')
            if next_url:
                obj = redirect(next_url)
            obj.set_cookie('whoami', 'wick',max_age=30)
            return obj
    return render(request, 'login.html')

(2)获取Cookie

  1. 获取cookie

    request.COOKIES.get('key')

  2. 获取加盐的cookie值

    request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)

    • default: 默认值
    • salt: 加密盐
    • max_age: 后台控制过期时间
from functools import wraps
def login_auth(func):
    @wraps(func)
    def inner(request,*args, **kwargs):
        print(request.path_info)
        print(request.get_full_path())
        # 判断当前用户是否登录
        if request.COOKIES.get('whoami'):
            res = func(request,*args, **kwargs)
            return res
        else:
            target_url = request.path_info
            return redirect(f'/login/?next={target_url}')
    return inner

(3)删除Cookie

obj.delete_cookie('key'):删除用户浏览器上之前设置的cookie值

@login_auth
def logout(request):
    obj = redirect('/login/')
    obj.delete_cookie('whoami')
    return obj

二、Session

(一)由来

  1. 解决Cookie的安全性和存储限制问题
  2. 服务器生成并保存在服务器,可以存储超过4096字节的键值对(依赖于Cookie)

(二)Django中操作Session

  1. Django中Session存储在django_session表中,第一次使用时需执行迁移命令
  2. Django中Session失效时间默认是14天
  3. 同一个浏览器生成的session都会保存在django_session表中同一条数据中

(1)设置Session

  • 设置Session时发生的事情:

    1. Django内部调用算法生成随机字符串
    2. 将随机字符串和加密后的数据(以及失效时间)存储到django_session表中
    3. 将随机字符串返回给浏览器,浏览器中以sessionid为键名保存为字典形式
  • 设置Session值

    request.session['k1'] = 123
    request.session.setdefault('k1',123) # 存在则不设置
  • 设置Session和Cooike的超时时间

    request.session.set_expiry(value)
    1. 如果value是个整数,session会在些秒数后失效。
    2. 如果value是个datatime或timedelta,session就会在这个时间后失效。
    3. 如果value是0,用户关闭浏览器session就会失效。
    4. 如果value是None,session会依赖全局session失效策略

(2)获取Session

  • 获取Session时发生的事情:

    1. Django内部会去请求头中获取cookie中的sessionid对应的随机字符串
    2. 将该字符串与django_session表中存储的随机字符串进行比对
    3. 比对成功,会把对应的加密后的数据获取出来,封装到request.session中,否则为空字典
  • 获取Session

    # 1. Session中数据
    request.session['k1']
    request.session.get('k1',None)
    
    # 2. 会话session的key
    request.session.session_key
    
    # 3. 检查会话session的key在数据库中是否存在
    request.session.exists("session_key")
    
    # 4. 所有 键、值、键值对
    request.session.keys()
    request.session.values()
    request.session.items()
    request.session.iterkeys()
    request.session.itervalues()
    request.session.iteritems()

(3)删除Session

  1. del request.session['k1']:删除Session中数据
  2. request.session.delete():删除当前会话的所有Session数据,数据库中也会删除
  3. request.session.flush() :删除当前的会话数据并删除会话的Cookie,数据库中也会删除
  4. request.session.clear_expired():将所有Session失效日期小于当前日期的数据删除,(Django中有自动动删除机制)

三、Django中间件

(一)什么是中间件

  1. 中间件是一个用来处理Django的请求和响应的框架级别的钩子,本质上是一个自定义类
  2. 可以全局范围内在视图函数执行前和施行后做一些额外的操作,比如用户登录校验、用户访问频率校验、用户权限校验
  3. Django中默认有七个中间件
# settings.py文件
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',
]

(二)自定义中间件

  1. 中间件可以定义五个方法
  2. 方法中参数request:必需参数,和视图函数中request一样
  3. 方法中返回值为None时, 继续按照django定义的规则向后继续执行
  4. 方法中返回值为HttpResponse对象时, 则直接将该对象返回给用户
# 在settings.py的MIDDLEWARE配置项中注册上述两个自定义中间件
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',
    'app01.mymiddleware.mymidd.MyMidd1',  # 自定义中间件MD1
    'app01.mymiddleware.mymidd.MyMidd2'  # 自定义中间件MD2
]

(1)process_request

  1. 默认按照自上而下的顺序执行MIDDLEWARE中每一个中间件内部的process_request方法
  2. 返回HttpResponse对象:请求会立刻停止从当前中间件的process_response 方法原路返回
# app01/mymiddleware/mymidd.py

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1里面的 process_request")

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

(2) process_response

  1. 在视图函数之后 ,默认按照自下而上的顺序执行每个中间件中的process_response 方法
  2. 必须给方法传入两个形参,request和response
  3. 必须返回response:response是视图函数返回的HttpResponse对象 ,浏览器才能接受HttpResponse对象
# app01/mymiddleware/mymidd.py

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

(3) process_view

  1. 执行视图函数之前触发(路由匹配之后)
  2. 返回 HttpResponse对象 : 直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
  3. 该方法有四个参数
    1. request: HttpRequest对象
    2. view_func:Django即将使用的视图函数对象
    3. view_args:将传递给视图的位置参数的列表.
    4. view_kwargs是将传递给视图的关键字参数的字典 (不包括request)
# app01/mymiddleware/mymidd.py
from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

(4) process_exception

  1. 视图函数出现错误异常自动触发,按照从下往上的顺序执行
  2. 两个参数 request 和 exception ( 视图函数异常产生的Exception对象 )
  3. 返回 HttpResponse对象 : 调用模板和中间件中的process_response方法,并返回给浏览器 (不会再执行process_exception 方法)

(5) process_template_response

  1. 视图函数执行完成后, 视图函数返回的对象有一个render()方法时才会执行,倒序执行
  2. 两个参数 request 和 response ,即必须返回response

(6)中间件执行流程

  1. 请求到达中间件, 先按照正序执行每个中间件的process_request方法 ,执行完后匹配路由
  2. 再顺序执行中间件中的process_view方法 ,执行完后执行视图函数
  3. 如果视图函数中有异常,会倒序执行process_exception
  4. 如果视图函数中返回对象有一个render()方法,会倒序执行process_exception方法
  5. 否则,会倒序执行process_response 方法,将响应发给客户端
  • 方法中有response参数的必须返回response(process_template_response、process_response )
  • 方法中没有response参数的(process_view、process_exception、process_response )
    1. 返回none:正常执行
    2. 返回HttpResponse的:会从当前中间件的response方法依次倒序执行,最终发送给客户端

以上,process_request、process_view默认为顺序,process_exception、process_exception、process_response 默认为倒序

四、Token(扩展)

(一)由来

客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比 ,对数据库和服务器压力大,因此产生了Token

(二)什么是Token

  1. Token是服务端生成的一串字符串
  2. 第一次登录时, 服务器生成一个Token返回给客户端
  3. 再次请求时, 客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码

(三)目的

减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮

(四)使用方式

(1) 设备号/设备mac地址作为Token

  1. 登录时将 设备号/mac地址 作为参数传给服务端
  2. 服务端接受后,用变量Token接收并保存在数据库,并将该Token设置到session中 ,下次请求只需比对变量Token
  • 优点: 只要登录一次以后一直可以使用
  • 缺点: 客户端需要带设备号/mac地址作为参数传递,而且服务器端还需要保存

  • 若服务器的Token超时后,将客户端传递的Token向数据库中查询,同时并赋值给变量Token,如此,Token的超时又重新计时

(2) session值作为Token

  1. 携带用户名和密码登陆
  2. 服务端接受后判断,如果正确, 将本地获取sessionID 作为Token返回给客户端
  • 好处:不用存储数据
  • 缺点:session过期后要重新登录

(五)一些问题和解决方案

  • 问题

    在网络不好或者并发请求时会导致多次重复提交数据

  • 解决方案( 将session和Token套用 )

    1. 可以对session进行加锁
    2. 把token放到session中,当后一个请求到来时,使用session中的token进行比对,当不一致时,即为重复提交,不允许通过

最新文章

  1. 号外!GNOME 3.22 正式发布喽!!!
  2. nio 弊端
  3. Zlib 在windows上的编译
  4. 九度OJ1085
  5. V&View更新
  6. 【转】 hive安装配置及遇到的问题解决
  7. SQL Server 2008中的hierarchyid
  8. Windows Azure 社区新闻综述(#69 版)
  9. 虎扯:小众玩物 webkit家的滚动条
  10. 【原创】leetCodeOj --- Dungeon Game 解题报告
  11. beanutils中Lazy
  12. 前端开发【第4篇:JavaScript基础】
  13. 深透清晰理解Java高并发概述
  14. Linux基础优化与安全归纳总结
  15. centos 7添加硬盘及LVM扩容
  16. codeforces 2A Winner (好好学习英语)
  17. py-faster-rcnn代码
  18. PMP用语集
  19. Spark2 Dataset之collect_set与collect_list
  20. html <meta>设置自动刷新或者几秒内跳转到指定页面

热门文章

  1. C# -- 多线程向同一文件写入
  2. Leetcode 之 Mysql(day01)
  3. Oracle 常用函数积累
  4. 30(1).原型聚类---k-means
  5. 22(8).模型融合---RegionBoost
  6. A:mysql数据库章节导航
  7. linux常用命令修改权限查看文档
  8. BZOJ2820/LG2257 YY的GCD 莫比乌斯反演
  9. python语言程序设计基础(第二版)第五章答案随笔
  10. FormData使用方法详解,以及在IE9环境下,前端的文件上传问题