zoukankan      html  css  js  c++  java
  • Django篇--->九

    cookie与session组件

    为什么会有cookie和session?
    由于http协议是无状态的,无法记住用户是谁

    cookie

    1.cookie的由来

    大家都知道HTTP协议是无状态的。

    无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

    一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

    状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

    2.什么是cookie

    存储在客户端浏览器上的键值对; 是服务端设置在客户端浏览器上的键值对。

    随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!

    3.cookie的工作原理

    cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

    4.cookie的覆盖

    如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。

    5.在浏览器中查看cookie F12打开开发者工具

    img

    6.cookie的操作

    #django必回三板斧
    	return HttpResponse()
    	return render()
    	return redirect()
    
    #实际上render和redirect都是HttpResponse对象
    #如何操作cookie
    #django返回给客户端浏览器的都必须是HttpResponse对象
    #操作cookie,首先要变形
        obj1 = HttpResponse()
        return obj1
    
        obj2 = render()
        return obj2
    
        obj3 = redirect()
        return obj3
    
    #设置cookie
    obj = HttpResponse()
    obj.set_cookie('key', 'value') #设置cookie就是在HttpResponse对象上
    return obj  #也相当于返回HttpResponse()
    
    # 获取cookie, 从request对象中获取
    request.COOKIE.get('key')  # 可以把 COOKIE 看成是一个字典
    
    # 删除cookie, 从HttpResponse对象上删除
    obj.delete_cookie('key')
    
    设置超时时间 (时间以秒为单位)
    max_age=None, 超时时间
    expires=None, 超时时间(用于IE浏览器)
    

    通过cookie写登录功能

    def login(request):  
        # print(request.path_info) #/login/  这个只拿到url,不拿get请求携带的参数
        # print(request.get_full_path()) #/login/?next=/index 这个即拿url也拿get请求携带的参数
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'jason' and password == '123':
                old_path = request.GET.get('next')
                if old_path:
                    obj = redirect(old_path)
                else:
                    obj = redirect('/home/')
                obj.set_cookie('name', 'jason')
                return obj
            else:
                return HttpResponse('账户密码错误')
        return render(request, 'login.html')
    
    # 登录装饰器
    from functools import wraps
    def login_auth(func):
        @wraps(func)
        def inner(request, *args, **kwargs): # 因为被装饰的对象都有参数 request,后面也要通过 request 取值,所以可以单独提取出来
            if request.COOKIES.get('name'):
                res = func(request, *args, **kwargs)
                return res
            else:
                target_path = request.path_info
                return redirect('/login/?next=%s'%target_path)
        return inner
    
    @login_auth
    def home(request):
        return HttpResponse('我是home主页,只有登录后才能访问')
    
    
    @login_auth
    def index(request):  
        return HttpResponse('我是index页面')
    
    @login_auth  # 注销cookie,先重定向到登录页面后再删除cookie
    def logout(request):
        obj = redirect('/login/')
        obj.delete_cookie('name')
        return obj
    

    request.path_info 和 request.get_full_path() 区别

    print(request.path_info) #/login/  这个只拿到url,不拿get请求携带的参数
    
    print(request.get_full_path()) #/login/?next=/index 这个即拿url也拿get请求携带的参数
    
    

    cookie加盐

    def salt_cookie(request):
        object = HttpResponse('ok')
        # 加盐,123是个密码,解cookie的时候需要它,
        object.set_signed_cookie('name','lqz',salt='123')# max_age,三天失效
        object.set_cookie('name','lqz',max_age=60*60*24*3)
        # path,只有访问shopping的时候,才会携带cookie过来
        object.set_cookie('name', 'lqz', path='/shopping/')
        object.set_cookie('name', 'lqz')
        return object
    

    加盐其他属性

    domain    设置域名下有效domain='map.baidu.com'
    expires     超时时间,传一个datatime对象
    secure=False       (默认是false,设置成True浏览器将通过HTTPS来回传cookie)
    httponly=True   只能https协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
    

    cookie总结:

    设置cookie两种方法:
    rep = HttpResponse(...)
    rep = render(request, ...)
    #第一种
    rep.set_cookie(key,value,...)
    #第二种
    rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
    
    
    获取cookie两种方法:
    #第一种
    request.COOKIES.get('key')
    #第二种
    request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    
    
    删除cookie方法:
    rep.delete_cookie('key')
    

    session

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

    django session默认的过期时间是14天

    session的使用

    # 设置session
    request.session['key'] = 'value', eg:如下
    request.session['name'] = 'jason'  # 通过中括号进行赋值
    # 设置的session键值对以字典的形式存储到 django_session表中的 session_data中
    
    # 设置session时:
    	1.django内部自动生成一个随机字符串
        2.将随机字符串和你要保存的数据写入 django_session表中(先在内存中生成一个记录,等到经过中间件的时候才会执行)
        session_key          session_data         	date
        随机字符串1              数据1            		...
        随机字符串2              数据2            		...
        随机字符串3              数据3            		...
        3.将产生随机的字符串发送到浏览器,让浏览器保存到cookie中
    		sessionid:随机字符串
            
    #取值
    request.session.get('key')  #取值通过get取,没有返回空,用[]取,没有就会报错
    #取值时:
    	1.django 内部会自动从请求信息中获取到随机字符串
        2.拿着随机字符串去 django_session表中比对
        3.一旦比对上了就将对应的数据拿出来放在 request.session中(request对象)
       
    #删除服务端上的session
    request.session.delete()
    
    #如何将浏览器和服务端全部删除(推荐用这个)
    request.session.flush()
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    

    设置,获取,删除

    # 设置session
    def set_session(request):
        request.session['name'] = 'nick'
        request.session['pwd'] = '123'
        request.session['age'] = 18
        request.session.set_expiry(value=0)  # 关闭浏览器就会失效
        return HttpResponse('设置session')
    
    # 获取session
    def get_session(request):
        print(request.session.get('name'))# nick
        print(request.session.get('pwd')) # 123
        print(request.session.get('age')) # 18
        return HttpResponse('获取session')
    
    #删除session
    def delete_session(request):
        request.session.delete()
        return HttpResponse('删除session')
    

    设置超时时间

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

    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中间件(******************)
    应用场景
    用户访问频率限制
    用户是否是黑名单、白名单
    所有用户登录校验
    只要是涉及到网站全局的功能,你就应该考虑使用中间件

    django中间件暴露给程序员五个可以自定义的方法(五个方法都是在特定的条件下自动触发的)

    自定义中间件

    1.新建一个文件夹 里面新建一个任意名称的py文件
                里面写类 固定继承
                from django.utils.deprecation import MiddlewareMixin
                class MyMiddle(MiddlewareMixin):
                    ...
    2.去配置文件注册到中间件配置中
                    你需要手写字符串的路径
                     'app01.mymiddleware.myaabb.MyMiddle1'
            
        
    需要掌握的
    	process_request(******):请求来的时候 会从上往下依次经过每一个中间件里面process_request,一旦里面返回了HttpResponse对象那么就不再往后执行了 会执行同一级别的process_response
        def process_request(self,request):
            print('我是第一个自定义中间件里面的process_request方法')
            return HttpResponse("我是第一个自定义中间件里面的HttpResponse对象返回值")  # 直接原地返回
        process_response(***):响应走的时候 会从下往上依次进过每一个中间件里面的process_response
        def process_response(self,request,response):  # response就是要返回给用户的数据
            print("我是第一个自定义中间件里面的process_response方法")
            return response
    了解的
    process_view:路由匹配成功之后执行视图函数之前触发
    process_exception:当视图函数出现异常(bug)的时候自动触发
    process_template_response:当视图函数执行完毕之后并且返回的对象中含有render方法的情况下才会触发
    
    

    中间件

    一、什么是中间件(相当用于 django中的保安)

    请求的时候需要先经过中间件才能到达django后端(urls,views,templates,models)

    响应的时候也需要经过中间件才能到达web服务网关接口

    img

    django默认的七个中间件

    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. 用于网站全局的身份检验,访问频率限制,权限校验......等等,只要是涉及到全局的校验你都可以在中间件里面完成
    2. django的中间件是所有Web框架中做的最好的

    三、中间件执行顺序

    -process_request()方法,从上往下执行
    	1.如果返回 HttpResponse对象,那么会直接返回,不再往下执行,而是跳到同级别的process_response方法,直接往回走
        2.请求来的时候,会经过每一个中间件里面的process_request方法(从上往下)
       
    -process_response()方法,从下往上执行
    	1.必须要将response形参返回,这个形参指代的就是返回前端的数据
        2.响应走的时候,会依次经过每一个中间件里面的process_response方法(从下往上)
    

    img

    img

    img

    自定义中间件

    在app01应用中创建一个任意的文件夹mymiddleware,再创建一个任意的py文件

    #1.导包
    from django.utils.deprecation import MiddlewareMixin
    #2.定义类,固定继承 MiddlewareMixin
    class MyMdd(MiddlewareMixin):
        def process_request(self, request):
            print('我是第一个中间件里面的process_request方法')
            
        def process_response(self, request, response):
            print('我是第一个中间件里面的process_response方法')
            return response
    
        
    class MyMdd2(MiddlewareMixin):
        def process_request(self,request):
            print('我是第二个中间件里面的process_request方法')
    
        def process_response(self,request,response):
            print('我是第二个中间件里面的process_response方法')
            return response
            
    

    3.定义视图函数

    def index(request):
        print("view函数...")
        return HttpResponse("OK")
    

    4.在settings中MIDDLEWARE注册自定义中间件

    MIDDLEWARE =[
         'mymiddleware.mdd.MyMdd',
         'mymiddleware.mdd.MyMdd1',
         'mymiddleware.mdd.MyMdd2',
    
    ]
    

    5.设置路由

    urlpatterns = [
        url(r'^index/',view.index)
    
    ]
    

    结果

    我是第一个中间件里面的process_request方法
    我是第二个中间件里面的process_request方法
    view函数...
    我是第二个中间件里面的process_response方法
    我是第一个中间件里面的process_response方法
    

    五、中间件五个可以自定义的方法

    需要掌握的方法:

    1.process_request()方法 请求来的时候会走该方法

    1.请求来的时候 会经过每个中间件里面的process_request方法(从上往下)
    2.如果方法里面直接返回了HttpResponse对象 那么会直接返回 不再往下执行
        基于该特点就可以做访问频率限制,身份校验,权限校验
    

    2.process_response()方法 响应回去的时候会走该方法

    1.必须将response形参返回 因为这个形参指代的就是要返回给前端的数据
    2.响应走的时候 会依次经过每一个中间件里面的process_response方法(从下往上)
    

    需要了解的方法:

    3.process_view()

    在路由匹配成功执行视图函数之前 触发
    

    4.process_exception()

    当你的视图函数报错时,就会自动执行
    

    5.process_template_response()

    当你返回的HttpResponse对象中必须包含render属性才会触发
    def index(request):
        print('我是index视图函数')
        def render():
            return HttpResponse('什么鬼玩意')
        obj = HttpResponse('index')
        obj.render = render
        return obj
    

    总结:

    你在书写中间件的时候,只要形参中有response,你就应该顺手将其返回,这个response就是要给前端的信息。
    
    这五种方法有哪些特点:
    1.都需要继承MiddlewareMixin
    2.在注册中间件的时候,在settings中写的路径不能错
    

    CSRF----跨站请求伪造

    1.什么是CSRF攻击

    攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的。

    比如钓鱼网站

    2.CSRF攻击原理

    img

    要完成一次CSRF攻击,受害者必须依次完成两个步骤:

      1.登录受信任网站A,并在本地生成Cookie。

      2.在不登出A的情况下,访问危险网站B。

    钓鱼网站例子

    通过制作一个跟正儿八经的网站一模一样的页面,骗取用户输入信息 转账交易从而做手脚
    转账交易的请求确确实实是发给了中国银行,账户的钱也是确确实实少了
    唯一不一样的地方在于收款人账户不对
    内部原理
        在让用户输入对方账户的那个input上面做手脚
        给这个input不设置name属性,在内部隐藏一个实现写好的name和value属性的input框
        这个value的值 就是钓鱼网站受益人账号
    如何区分钓鱼网站和正经网站?在正经网站返回页面的时候,在form表单中偷偷塞一个特殊的字符串,后端记下该页面对应的字符串的值,等用户发post请求来的时候,我先去校验特殊的字符串是否匹配
    
    如何去写这个特殊的字符串呢?模版语法有一个固定的写法{% csrf_token %},必须写在form表单内
    

    3.CSRF攻击防范

    防止钓鱼网站的思路
        网站会给用户访问的form表单页面 偷偷塞一个随机字符串
        请求到来的时候 会先比对随机字符串是否一致  如果不一致  直接拒绝(403)
    

    该随机字符串有以下特点
      1.同一个浏览器每一次访问都不一样
      2.不同浏览器绝对不会重复

    4.CSRF在Django中的应用

    1.在form表单中使用

    <form action="" method="post">
        {% csrf_token %}
        <p>username:<input type="text" name="username"></p>
        <p>password:<input type="text" name="password"></p>
        <input type="submit">
    </form>
    

    2.在ajax中使用,如何避免csrf校验

    • 有两种方法,通过修改ajax的data

      方法一:先在form表单页面上写{% csrf_token%},利用标签查找,获取到该input键值消息

    data{'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
    

      方法二:直接书写'{{csrf_token}}'

    data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
    

    方法三:

    • 还可以将获取随机键值对的方法,写到一个js文件中,然后导入这个文件即可

      新建一个js文件,存放以下代码

    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                 // Does this cookie string begin with the name we want?
                 if (cookie.substring(0, name.length + 1) === (name + '=')) {
                     cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                     break;
                 }
            }
         }
         return cookieValue;
    }
    var csrftoken = getCookie('csrftoken');
    
    
    function csrfSafeMethod(method) {
       // these HTTP methods do not require CSRF protection
       return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
     }
    
    $.ajaxSetup({
         beforeSend: function (xhr, settings) {
           if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
             }
         }
     });
    

    5.CSRF禁用

    1.CSRF全局禁用

    注释掉settings中MIDDLEWARE的中间件 'django.middleware.csrf.CsrfViewMiddleware',

    2.CSRF局部禁用

    • 在FBV中,直接加载FBV就行了 (在页面不加csrf_token情况下)
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    #(前提是全局使用,没有注释csrf)  让这个不用校验,可以局部使用
    #当你网站全局需要校验csrf的时候,有几个不需要校验该如何处理
    @csrf_exempt
    def login(request):
       return HttpResponse('login')
    
    #(前提是全局禁用,注释csrf,不会进行校验)  设置就会进行校验,局部禁用
    #当你网站全局不校验csrf的时候,有几个需要校验又该如何处理
    @csrf_protect    
    def lll(request):
       return HttpResponse('lll')
    
    • 在CBV中,只能在dispatch方法或者类上面
    from django.views import View
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
    from django.utils.decorators import method_decorator
    # 这两个装饰器在给CBV装饰的时候 有一定的区别
    如果是csrf_protect 那么有三种方式
        # 第一种方式
        # @method_decorator(csrf_protect,name='post')  # 有效的
        class MyView(View):
            # 第三种方式
            # @method_decorator(csrf_protect)
            def dispatch(self, request, *args, **kwargs):
                res = super().dispatch(request, *args, **kwargs)
                return res
    
            def get(self,request):
                return HttpResponse('get')
            # 第二种方式
            # @method_decorator(csrf_protect)  # 有效的
            def post(self,request):
                return HttpResponse('post')
    
    如果是csrf_exempt 只有两种(只能给dispatch装)   特例
    @method_decorator(csrf_exempt,name='dispatch')  # 第二种可以不校验的方式
    class MyView(View):
        # @method_decorator(csrf_exempt)  # 第一种可以不校验的方式
        def dispatch(self, request, *args, **kwargs):
            res = super().dispatch(request, *args, **kwargs)
            return res
    
        def get(self,request):
            return HttpResponse('get')
    
        def post(self,request):
            return HttpResponse('post')
    
  • 相关阅读:
    such用法
    divorce用法
    towel用法
    go for用法
    catch on用法
    incredibly用法
    mess用法
    dentist用法
    steer clear of用法
    incredible
  • 原文地址:https://www.cnblogs.com/chmily/p/11768619.html
Copyright © 2011-2022 走看看