• Django(六)Session、CSRF、中间件


    大纲

    二、session
    1、session与cookie对比
    2、session基本原理及流程
    3、session服务器操作(获取值、设置值、清空值)
    4、session通用配置(在配置文件中)
    5、session引擎配置(db、cache、file、cookie加密)
    三、CSRF
    1、csrf原理-form提交及ajax提交
    2、csrf全局与局部应用配置
    四、中间件生命周期
    1、process_request、process_response
    下面自己创建一个中间件
    2、process_view
    3、其他
    一、内容回顾

    二、session

    1、session与cookie对比

    Cookie:保存在用户浏览器端的键值对

    本地可以修改;如果有敏感信息,可以被看到。

    基于cookie做用户验证时,敏感信息不适合放在cookie中
    把存储压力放到每个客户端上,对于服务器端压力小了。

    Session:保存在服务器端的键值对

    服务端:保存键值对{'随机字符串':{……用户信息……}},通过cookie保存随机字符串到客户端上。

    使用session前:先执行 python manage.py makemigrations   , python manage.py migrate,因为默认Django session 保存在数据库中的django_session表中。

    2、session基本原理及流程

    • session 设置值

      • request.session['is_login'] = True  Django会执行一下操作
       # 生成随机字符串
       # 写到用户浏览器cookie
       # 保存到session中
       # 在随机字符串对应的字典中设置相关内容……
    • session 获取值

      • request.session['is_login']: 执行以下操作
      # 获取当前用户的随机字符串
      # 根据随机字符串获取对应信息
    • 使用session前,别忘了先生成数据库   python manage.py migrate

    urls.py

        url(r'^login/', views.login),
        url(r'^index/', views.index),
        url(r'^logout/$', views.logout),

    views.py

    def login(request):
        if request.method == "GET":
            return render(request,'login.html')
        elif request.method == "POST":
            user = request.POST.get('user')
            pwd = request.POST.get('pwd')
            if user == 'root' and pwd == "123":
            # session中设置值
                # 生成随机字符串
                # 写到用户浏览器cookie
                # 保存到session中
                # 在随机字符串对应的字典中设置相关内容……
                request.session['username'] = user
                request.session['is_login'] = True
                if request.POST.get('rmb',None) == '1':
                    # 超时时间
                    request.session.set_expiry(10)
                return redirect('/index/')
            else:
                return render(request,'login.html')
    
    def index(request):
        # 获取当前用户的随机字符串
        # 根据随机字符串获取对应信息
        if request.session.get('is_login',None):
            return HttpResponse(request.session['username'])
        else:
            return HttpResponse("请登录……")
    
    def logout(request):
        # del request.session['username']
        request.session.clear()    # 清除session,注销
        return redirect('/login/')

    login.html

    <body>
        <form action="/login/" method="POST">
            <input type="text" name="user" />
            <input type="text" name="pwd" />
            <input type="checkbox" name="rmb" value="1" /> 10秒免登录
            <input type="submit" value="提交" />
        </form>
    </body>>  

    index.html

    <body>
        <h1>欢迎登录:{{ username }}, {{ request.session.username }}</h1>
    <!----------  这里不用使用后台模板传值,使用session也能获取到用户名  -------------->
        <a href="/logout/">注销</a>
    </body>

    3、session服务器操作(获取值、设置值、清空值)

     # 获取、设置、删除Session中数据
            request.session['k1']               # 获取
            request.session.get('k1',None)
            request.session['k1'] = 123         # 设置
            request.session.setdefault('k1',123) # 存在则不设置
            del request.session['k1']           # 删除
    
        # 所有 键、值、键值对
            request.session.keys()
            request.session.values()
            request.session.items()
            request.session.iterkeys()
            request.session.itervalues()
            request.session.iteritems()
    
        # 获取用户session的随机字符串
            request.session.session_key
        # 将所有Session失效日期小于当前日期的数据删除
            request.session.clear_expired()
        # 检查 用户session的随机字符串 在数据库中是否存在
            request.session.exists("session_key")
        # 删除当前用户的所有Session数据
            request.session.delete("session_key")
            request.session.clear()     # 比delete用法更简单,注销的时候可以使用
    
    # 设置超时时间 (默认session超时时间是两周)
        request.session.set_expiry(value)
            # * 如果value是个整数,session会在些秒数后失效。
            # * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            # * 如果value是0,用户关闭浏览器session就会失效。
            # * 如果value是None,session会依赖全局session失效策略。

    4、session通用配置(在配置文件中)

    settings.py

    SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 引擎(默认)
    
    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,默认修改之后才保存(默认)
    # 这个好。settings里设为true,超时时间按照最后一次客户端请求计算,如上按照最后一次请求之后10秒失效。

    5、session引擎配置(db、cache、file、cookie加密)

    Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

    • 数据库(默认)
    • 缓存
    • 文件
    • 缓存+数据库
    • 加密cookie
    # 数据库Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
    
    # 缓存Session    
        SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
        SESSION_CACHE_ALIAS = 'default'  # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
        # 连接memcache 的配置,缓存部分会提到。不支持redis,连它需要安装插件
    
    # 文件Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
        SESSION_FILE_PATH = os.path.join(BASE_DIR, 'cache')  # 放到cache目录下
        SESSION_FILE_PATH = None   # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
        # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
    
    # 缓存+数据库Session
        SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'  # 引擎
    
    # 加密cookie Session (都放到cookie里面,只是做了加密处理)
        SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'  # 引擎

    三、CSRF

    之前提到的xss攻击:网站评论里等允许别人写js的时候,别人进行的恶意操作。csrf类似。

    CSRF的防护通常有两种方式,一个是通过Challenge-Response的方式,例如通过Captcha和重新输入密码等方式来验证请求是否伪造,但这会影响用户体验,类似银行付款会采用这样的方式。另一种是通过随机Token的方式,多数Web系统都会采用这种方式,Django也是用的这种。

    1、csrf原理-form提交及ajax提交

    客户端get请求时,django会生成随机字符串给客户,当客户端提交form表单的时候,如果没有随机字符串,则django不允许,报403错误。

    form提交数据需要带随机字符串过去,Ajax提交也需要带着过去。ajax带哪的值?

    加上{% csrf_token %}在html form里生成了一个,在也生成了一份。浏览器审查元素 –> Network –> Cookies 里也能找到随机字符串。

    所以ajax提交的时候,只需要把cookie里的随机字符串拿到,放到请求头里面发过去就可以了

    后台获取随机字符串的key,key是什么值? x-CSRFtoken

    login.html

    <body>
        <!-- form 提交 -->
        <form action="/login/" method="POST">
            <!-- 生成 csrf 的随机字符串 -->
            <!-- {{ csrf_token }} -->
            {% csrf_token %}  <!--html里会自动生成隐藏input框-->
            <input type="text" name="user" />
            <input type="text" name="pwd" />
            <input type="checkbox" name="rmb" value="1" /> 10秒免登录
            <input type="submit" value="提交" />
            <input id="btn" type="button" value="Ajax提交" />
        </form>
    
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/jquery.cookie.js"></script>
        <script>
            $(function(){
                // ajax 提交
                $.ajaxSetup({  // 对整个页面所有的ajax操作做个配置
                    beforeSend: function(xhr,settings){  // 发送ajax前的操作
                        xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
                    }
                });
    
                $('#btn').click(function () {
                    $.ajax({
                        url: '/login/',
                        type:"POST",
                        data: {'user': 'root', 'pwd': '123'},
            // 这种方式加请求头,每个操作都加麻烦一些,所以使用上面的  ajaxSetup 里加
                        // headers: {'X-CSRFtoken': $.cookie('csrftoken')},
                        success:function(arg){
    
                        }
                    })
                });
            })
        </script>
    </body>

    这里ajax提交,在浏览器审查元素、network里看效果。

    2、csrf全局与局部应用配置

    settings里面,允许csrf验证,那么就是对全局都进行验证。如果个别的方法不需要使用,怎么单一配置?

    django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

    全局:

      中间件 django.middleware.csrf.CsrfViewMiddleware

    局部:

    • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
    • @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

    注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

    对于ajax中,采用ajaxSetup方式也是全局都加,比如get方式是不需要的,可以如下配置

    var csrftoken = $.cookie('csrftoken');
    
            function csrfSafeMethod(method) {
                // these HTTP methods do not require CSRF protection
                return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
            }
            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    // settings 会获取到ajax里面的所有配置
                    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken);
                    }
                }
            });
            function Do(){
    
                $.ajax({
                    url:"/app01/test/",
                    data:{id:1},
                    type:'POST',
                    success:function(data){
                        console.log(data);
                    }
                });
    
            }

    四、中间件生命周期

    settings里的 MIDDLEWARE 都是一个一个的中间件。客户端请求,先经过一排一排的中间件到达views,之后再通过中间件返回给客户端。而通过中间件都是调用中间件的某个方法、

    1、process_request、process_response

    • process_request:客户端请求,经过中间件的方法
    • process_response:返回客户端,经过中间件的方法

    下面自己创建一个中间件

    随便创建一个目录middle,

    middle/m.py

    from django.utils.deprecation import MiddlewareMixin
    
    class Row1(MiddlewareMixin):
        def process_request(self, request):
            print("中间件1")
        def process_response(self, request, response):
            print("中间件1返回")
            return response
    # 参数里的 response :就是views里面返回的值,所以要继续返回一下,否则客户端收不到数据
    from django.shortcuts import HttpResponse
    class Row2(MiddlewareMixin):
        def process_request(self, request):
            print("中间件2")
            # return HttpResponse("禁止你访问")
        def process_response(self, request, response):
            print("中间件2返回")
            return response
    
    class Row3(MiddlewareMixin):
        def process_request(self, request):
            print("中间件3")
        def process_response(self, request, response):
            print("中间件3返回")
            return response

    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',
        'middle.m.Row1',
        'middle.m.Row2',
        'middle.m.Row3',
    ]

    views.py

    def test(request):
        print("最终返回信息")
        return HttpResponse("OK")

    中间件里面的参数request里接受的数据和views里接受的数据是一样的,所以从里面取值可以做相应的判断处理,允不允许数据通过。

    比如上面的示例:中间件row2,process_request,里返回数据,则会在同级的process_response里开始返回数据给客户端了。

    注意:这是在Django1.10版本才这样的,之前版本:row2返回数据,会在最底部的response开始网上返回数据,这里是row3。

    所以中间件是对所有的请求做统一操作,比如数据校验、黑名单过滤

    2、process_view

    如下:每个中间件类中中加入如下方法:

    class Row1(MiddlewareMixin):
        def process_request(self, request):
            print("中间件1")
        def process_view(self, request, view_func, view_func_args, view_func_kwargs):
            # view_func 对应 views函数,view_func_args、kwargs 对应 views里的参数、
            print("中间件1view")
        def process_response(self, request, response):
            print("中间件1返回")
            return response

    请求顺序如下:用户请求 –> 每个中间件的request –> 到达urls路由匹配,匹配成功后 –> 折回每个中间件的view –> views –> 通过response返回

    3、其他

    def process_exception(self, request, exception):
        if isinstance(exception, ValueError):
            return HttpResponse("出现异常")
    # 异常处理 views函数里出错了,执行这里,如views里 int('lgeng')

    views函数如果出现异常,返回会找exception方法,一级一级往上找,如果有处理返回,如果都没有处理就直接返回报错了。

    process_template_response(self,request,response)
    # 如果views中的函数返回的对象中,具有render方法,执行这个方法。

    一、内容回顾

    1、基本生命周期

    2、URL

        /index/                 index
        /list/(d+)             index
        /list/(d+) name='li'   index
        /list/(d+) include     index

    3、views

    # 所有内容的原生值
    request.body        # 所有的post请求,都会放到body里面传过去
        request.POST        # 从request.body中提取
        request.GET         # 从request.body中提取
        request.FILES
        request.xxxx.getlist
    # 请求头内容
    request.Meta
        request.method(POST,GET,PUT)
        request.path_info
        request.COOKIES
        ……
    
    # 返回数据 #########
        return HttpResponse # 支持返回字符串 和 bytes类型
        return render       # 渲染页面
        return redirect     # 跳转
    
        # 返回cookie ##
        response = HttpResponse('ok')
        response.set_cookie()
        return response
        # 把cookie,放到响应头里面,客户端浏览器去响应头里面获取。。所以也能设置响应头内容
        response['name'] = 'lgeng'

    4、Model操作(原生Sql也可以)

    # 表内容操作:
        models.TB.objects.create()
        obj = models.TB(..)
        obj.save()
        models.TB.objects.all()[7:10]  # 切片
        models.TB.objects.update()
        models.TB.objects.filter()
        models.TB.objects.delete
        models.TB.objects.values
        models.TB.objects.values_list
        models.TB.objects.get
        models.TB.objects.filter().update()
        models.TB.objects.filter().first()
        models.TB.objects.filter(**{}).count()
        models.TB.objects.filter(双下划线跨表)
        models.TB.objects.filter(id__gte=1)
        models.TB.objects.exclude(id__lt=1)
    
        models.TB.objects.filter(id_in=[1,2,3]) # id__in
    
        # 多对多
            obj.set
            obj.add([1,2,3])
            obj.add(1,2,3)
            obj.remove([1,2,3])
            obj.clear()
    
    
        models.TB.objects.all()
        [obj,obj]
        obj.fk.name
        models.TB.objects.all().order_by('')
        models.TB.objects.distinct()
    
        ## 跨表操作 ###########
        class A:
            name ...
            # 而A表操作B表,通过表名+‘_set’ --> b_set
        class B:
            caption ...
            fk = ForignKey(A)   # B表操作A表,通过 fk.

    5、模板语言

    # 基本操作
        def func(request):
            return render(request,'index.html',{'val':[1,2,3]})
        # index.html 里
        <h1>{{ val.0 }}</h1>
    # 继承    
        extends "layout.html"
    # include
        组件
    # simpli_tag, filter

    转载请务必保留此出处:http://www.cnblogs.com/lgeng/articles/7365891.html

    <!-- END  -->

    《版本说明》: 本文转自 -- http://blog.csdn.net/fgf00/article/details/54299199

  • 相关阅读:
    第一节,Django+Xadmin打造上线标准的在线教育平台—创建用户app,在models.py文件生成3张表,用户表、验证码表、轮播图表
    Tensorflow 错误:Unknown command line flag 'f'
    Python 多线程总结
    Git 强制拉取覆盖本地所有文件
    Hive常用函数 傻瓜学习笔记 附完整示例
    Linux 删除指定大小(范围)的文件
    Python 操作 HBase —— Trift Trift2 Happybase 安装使用
    梯度消失 梯度爆炸 梯度偏置 梯度饱和 梯度死亡 文献收藏
    Embedding 文献收藏
    深度学习在CTR预估中的应用 文献收藏
  • 原文地址:https://www.cnblogs.com/lgeng/p/7365891.html
Copyright © 2020-2023  润新知