• 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

  • 相关阅读:
    Test SLURM
    PGM_Foundations
    PGM-Introduction
    Functional Analysis-Metric Space
    Introduction to Machine Learning
    Time Series Analysis
    Solving Puzzles With HHT From a Engineering Perspective
    Docker-3:Data Volume
    Docker-2:network containers
    Docker-1
  • 原文地址:https://www.cnblogs.com/lgeng/p/7365891.html
Copyright © 2020-2023  润新知