zoukankan      html  css  js  c++  java
  • WEB框架Django进阶--cookie/session/CSRF

    cookie

    浏览器客户端的一个文件,

    通常是以字典的形式存放的,

    通常我们登录网站后会显示当前登录的用户名,实现的例子的views:

    user_info = {
        "alex":{"pwd":"123"},
        "jack":{"pwd":"456"}
    }
    def login(request):
        if request.method == "GET":
            return render(request,"login.html")
        if request.method == "POST":
            u = request.POST.get("username")
            p = request.POST.get("pwd")
            dic = user_info.get(u)
            if not dic:
                return render(request,"login.html")
    
            if dic["pwd"] == p:
                res = redirect("/index/")
                res.set_cookie("user",u)
                return res
            else:
                return render(request,"login.html")
    
    
    def index(request):
    #获取当前登录的用户名
        v = request.COOKIES.get("user")
        if not v:
            return render(request,"login.html")
    
        return render(request,"index.html",{"v":v})

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>当前用户{{ v }}</h1>
    </body>
    </html>
    

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="/login/" method="POST" enctype="multipart/form-data">
            <p>
                <input type="text" name="username" placeholder="用户名" />
            </p>
            <p>
                <input type="password" name="pwd" placeholder="密码" />
            </p>
            <p>
                <input type="submit" value="提交" />
            </p>
        </form>
    </body>
    </html>

    这种情况下用户在没有登录的时候是无法访问index页面的,用户登录成功后就能显示初恋当前的用户了,也就实现了用户的认证

    设置Cookie:
    
    res.set_cookie(key,value)
    
    参数:
    
    key               键
    
    value=‘’          值
    
    max_age=None      超时时间,以秒作为单位。默认是关闭浏览器失效
    
    expires=None      超时时间,这个是可以设置datatime
    
    path="/"          Cookie生效的路径
    
    domain=None       Cookie生效的域名
    
    secure=False      https传输
    
    httponly=False    只能http协议传输,无法被JavaScript获取
    
    salt=“jiami”      通过salt这个参数实现加密,同样的获取cookie的时候也需要加上salt参数才能进行解密
    

      

    基于cookie的实现分页定制显示每页数据条数

    user_list.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .q .page{
                display: inline-block;
                background-color: aqua;
                margin: 5px;
                padding: 5px;
            }
            .q .page.active{
                background-color: red;
                color: white;
            }
            .q #i1{
                padding-left: 5px;
                color: red;
            }
        </style>
    </head>
    <body>
        <ul>
            {% for item in list %}
                {% include "tag.html" %}
            {% endfor %}
        </ul>
        <div>
            <select id="ll" onchange="changePageSize(this)">
                <option value="5">5</option>
                <option value="10">10</option>
                <option value="20">20</option>
                <option value="50">50</option>
                <option value="100">100</option>
            </select>
        </div>
        <div class="q">
            {{ temp|safe }}
            <input type="text" />
            <a onclick="Go(this,'/user_list/?p=');" id="i1">GO</a>
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/jquery.cookie.js"></script>
        <script>
            function Go(th,base) {
                 var val = th.previousElementSibling.value;
                location.href = base + val
            }
    
            $(function () {
                var v =$.cookie("per_page_num");
                $("#ll").val(v);
    
            });
    
            function changePageSize(th) {
                var v = $(th).val();
                console.log(v);
                $.cookie("per_page_num",v);
                location.reload();
            }
    
        </script>
    </body>
    </html>

    views.py

    def user_list(request):
        current_page = request.GET.get("p",1)
        current_page = int(current_page)   #当前页
    
        res = request.COOKIES.get("per_page_num")
        res = int(res)
        page_obj = pagination.Page(current_page,len(list),res)
        
        
        data=list[page_obj.start:page_obj.end]
        page_str = page_obj.page_str("/user_list/")
        return render(request,"user_list.html",{"list":data,"temp":page_str})

    显示效果如下:其中利用了jquery和jquery.cookie

    用户认证装饰器

    FBV的装饰器用法

    我们前面的代码中有用户登录的:

    def index(request):
        #获取当前登录的用户名
        v = request.COOKIES.get("username1")
        if not v:
            return redirect("/login/")
        return render(request,"index.html",{"current_user":v})

    当我们有很多页面都需要用户登录验证的时候,每个都这样写的话,就出现了代码重复,所以这里我们可以用装饰器实现,上面代码修改一下就可以。

    修改后:

    def auth(func):
        def inner(request,*args,**kwargs):
            v = request.COOKIES.get("username1")
            if not v:
                return redirect("/login/")
            return func(request,*args,**kwargs)
        return inner
    
    @auth
    def index(request):
        #获取当前登录的用户名
        v = request.COOKIES.get("username1")
        return render(request,"index.html",{"current_user":v})

    CBV的装饰器用法

    下面是一个普通的CBV的例子:

    from django import views
    class Order(views.View):
    
        def get(self,request):
            v = request.COOKIES.get("username1")
            if not v:
                return redirect("/login/")
            return render(request, "index.html", {"current_user": v})
        def post(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})

    我们只对get请求做认证,当我们访问Order时候就加上了认证

    def auth(func):
        def inner(request,*args,**kwargs):
            v = request.COOKIES.get("username1")
            if not v:
                return redirect("/login/")
            return func(request,*args,**kwargs)
        return inner
    from django import views
    from django.utils.decorators import method_decorator
    class Order(views.View):
        @method_decorator(auth)
        def get(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})
        
        def post(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})

    这种方法只针对于get,如果我们想给更多的方法加上认证,还可以修改成这样的:

    def auth(func):
        def inner(request,*args,**kwargs):
            v = request.COOKIES.get("username1")
            if not v:
                return redirect("/login/")
            return func(request,*args,**kwargs)
        return inner
    
    from django import views
    from django.utils.decorators import method_decorator
    class Order(views.View):
        @method_decorator(auth)
        def dispatch(self, request, *args, **kwargs):
            return super(Order,self).dispatch(request, *args, **kwargs)
    
        def get(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})
    
        def post(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})

     每次执行CBV是必须先执行一个dispatch方法,我们在dispatch加上认证,就相当于在所有上面加上认证,

    我们还可以简化成:

    @method_decorator(auth,name="dispatch")
    class Order(views.View):
        def get(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})
    
        def post(self,request):
            v = request.COOKIES.get("username1")
            return render(request, "index.html", {"current_user": v})
    

    session

    session的原理

    基于cookie做用户验证的时候,敏感信息不适合放在cookie中

    session依赖cookie

    cookie是保存在用户浏览器端的键值对

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

    session在服务器端的数据存放为:

    session={
        随机字符串1:{用户1的相关信息},
        随机字符串2:{用户2的相关信息}, 
        随机字符串3:{用户3的相关信息},
    }

    session的客户端就是浏览器的客户端中cookie中存放的数据是当前用户对应的随机字符串。

    session的工作过程

    过程:  

      1、生成随机字符串

      2、写到用户浏览器的cookie中

      3、保存到session中

      4、在随机字符串对应的字典中设置相关内容

    而上述过程在Django中的体现为:views.py

    from django.shortcuts import render,redirect,HttpResponse
    
    def login(request):
        if request.method == "GET":
            return render (request,"login.html")
        elif request.method == "POST":
            user = request.POST.get("username")
            pwd = request.POST.get("pwd")
            if user == "root" and pwd == "123":
                request.session["username"] = user
                request.session["is_login"] = True
                return redirect("/index/")
            else:
                return render(request, "login.html")
    
    
    def index(request):
        if request.session["is_login"]:
            return HttpResponse(request.session["username"])
        else:
            return HttpResponse("REEOR") 

    request.session["username"]=user

    这里的username为通过request.POST.get("username")从前端html页面中获取到的用户名信息

    注意:

    在Django中要用session中一定要先执行:

    python manage.py makemigrations

    python manage.py migrate

    因为当用户登录的时候的就会在数据库的django_session表中记录session信息

    同样的通过request.session["username"]也可以获取相应的值

    在这个过程中:

    1、首先获取当前用户的随机字符串

    2、根据随机字符串获取对应的内容

    session的操作

     1 request.session["k1"]  如果不存在则会报错
     2 
     3 request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none)
     4 
     5  
     6 
     7 request.session['k1'] = 123 设置session值
     8 
     9 request.session.setdefault('k1',123)  存在则不设置
    10 
    11 del request.session['k1']  删除
    12 
    13 request.session.clear()    删除
    14 
    15  
    16 
    17 所有 键、值、键值对
    18 
    19 request.session.keys()
    20 
    21 request.session.values()
    22 
    23 request.session.items()
    24 
    25 request.session.iterkeys()
    26 
    27 request.session.itervalues()
    28 
    29 request.session.iteritems()
    30 
    31  
    32 
    33 用户session的随机字符串
    34 
    35 request.session.session_key
    36 
    37  
    38 
    39 将所有Session失效日期小于当前日期的数据删除
    40 
    41 request.session.clear_expired()
    42 
    43  
    44 
    45 检查 用户session的随机字符串 在数据库中是否
    46 
    47 request.session.exists("session_key")
    48 
    49  
    50 
    51 删除当前用户的所有Session数据
    52 
    53 request.session.delete("session_key")
    54 
    55  
    56 
    57 request.session.set_expiry(value)
    58 
    59 默认的过期时间是两周,如果自己设置了过期时间,这样自己设定的优先级就会高于默认的
    60 
    61 如果value是个整数,session会在些秒数后失效。
    62 
    63 如果value是个datatime或timedelta,session就会在这个时间后失效。
    64 
    65 如果value是0,用户关闭浏览器session就会失效。
    66 
    67 如果value是None,session会依赖全局session失效策略。
    68 
    69  
    70 
    71 配置setting.py
    72 
    73 SESSION_COOKIE_NAME = "sessionid"      # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    74 
    75 SESSION_COOKIE_PATH = "/"              # Session的cookie保存的路径(默认)
    76 
    77 SESSION_COOKIE_DOMAIN = None             # Session的cookie保存的域名(默认)
    78 
    79 SESSION_COOKIE_SECURE = False          # 是否Https传输cookie(默认)
    80 
    81 SESSION_COOKIE_HTTPONLY = True         # 是否Session的cookie只支持http传输(默认)
    82 
    83 SESSION_COOKIE_AGE = 1209600             # Session的cookie失效日期(2周)(默认)
    84 
    85 SESSION_EXPIRE_AT_BROWSER_CLOSE = False    # 是否关闭浏览器使得Session过期(默认)
    86 
    87 SESSION_SAVE_EVERY_REQUEST = False        # 是否每次请求都保存Session,默认修改之后才保存(默认)
    View Code

    Django中对于session的存储方式

     1 Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
     2  
     3 a. 配置 settings.py
     4  
     5     SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
     6      
     7     SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
     8     SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
     9     SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
    10     SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
    11     SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
    12     SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
    13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
    14     SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
    15  
    16  
    17  
    18 b. 使用
    19  
    20     def index(request):
    21         # 获取、设置、删除Session中数据
    22         request.session['k1']
    23         request.session.get('k1',None)
    24         request.session['k1'] = 123
    25         request.session.setdefault('k1',123) # 存在则不设置
    26         del request.session['k1']
    27  
    28         # 所有 键、值、键值对
    29         request.session.keys()
    30         request.session.values()
    31         request.session.items()
    32         request.session.iterkeys()
    33         request.session.itervalues()
    34         request.session.iteritems()
    35  
    36  
    37         # 用户session的随机字符串
    38         request.session.session_key
    39  
    40         # 将所有Session失效日期小于当前日期的数据删除
    41         request.session.clear_expired()
    42  
    43         # 检查 用户session的随机字符串 在数据库中是否
    44         request.session.exists("session_key")
    45  
    46         # 删除当前用户的所有Session数据
    47         request.session.delete("session_key")
    48  
    49         request.session.set_expiry(value)
    50             * 如果value是个整数,session会在些秒数后失效。
    51             * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    52             * 如果value是0,用户关闭浏览器session就会失效。
    53             * 如果value是None,session会依赖全局session失效策略。
    数据库session
     1 a. 配置 settings.py
     2  
     3     SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
     4     SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
     5  
     6  
     7     SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
     8     SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
     9     SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
    10     SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
    11     SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
    12     SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
    13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
    14     SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存
    15  
    16  
    17  
    18 b. 使用
    19  
    20     同上
    缓存session
     1 a. 配置 settings.py
     2  
     3     SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
     4     SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
     5  
     6  
     7     SESSION_COOKIE_NAME = "sessionid"                          # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
     8     SESSION_COOKIE_PATH = "/"                                  # Session的cookie保存的路径
     9     SESSION_COOKIE_DOMAIN = None                                # Session的cookie保存的域名
    10     SESSION_COOKIE_SECURE = False                               # 是否Https传输cookie
    11     SESSION_COOKIE_HTTPONLY = True                              # 是否Session的cookie只支持http传输
    12     SESSION_COOKIE_AGE = 1209600                                # Session的cookie失效日期(2周)
    13     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                     # 是否关闭浏览器使得Session过期
    14     SESSION_SAVE_EVERY_REQUEST = False                          # 是否每次请求都保存Session,默认修改之后才保存
    15  
    16 b. 使用
    17  
    18     同上
    文件session
    1 数据库用于做持久化,缓存用于提高效率
    2  
    3 a. 配置 settings.py
    4  
    5     SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
    6  
    7 b. 使用
    8  
    9     同上
    缓存+数据库session
    1 a. 配置 settings.py
    2      
    3     SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎
    4  
    5 b. 使用
    6  
    7     同上
    加密cookie session

    扩展:Session用户验证

    def login(func):
        def wrap(request, *args, **kwargs):
            # 如果未登陆,跳转到指定页面
            if request.path == '/test/':
                return redirect('http://www.baidu.com')
            return func(request, *args, **kwargs)
        return wrap
    

    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

    原理

    当用post提交数据的时候,django会去检查是否有一个csrf的随机字符串,如果没有就会报错,这也是之前我们一直将其注释的原因,错误如下:

    在django内部支持生成这个随机字符串

    通过form提交

    在form表单里面需要添加{%csrf_token%}

    这样当你查看页面源码的时候,可以看到form中有一个input是隐藏的

    总结原理:当用户访问login页面的时候,会生成一个csrf的随机字符串,并且cookie中也存放了这个随机字符串,当用户再次提交数据的时候会带着这个随机字符串提交,如果没有这个随机字符串则无法提交成功

    cookie中存放的csrftoken如下图

    通过ajax提交

    因为cookie中同样存在csrftoken,所以可以在js中通过:

    $.cooke("cstftoken")获取

    如果通过ajax进行提交数据,这里提交的csrftoken是通过请求头中存放,需要提交一个字典类型的数据,即这个时候需要一个key。

    在views中的login函数中:from django.conf import settings,然后打印print(settings.CSRF_HEADER_NAME)

    这里需要注意一个问题,这里导入的settings并不是我们在项目文件下看到的settings.py文件,这里是是一个全局的settings配置,而当我们在项目目录下的settings.py中配置的时候,我们添加的配置则会覆盖全局settings中的配置

    print(settings.CSRF_HEADER_NAME)打印的内容为:HTTP_X_CSRFTOKEN

    这里的HTTP_X_CSRFTOKEN是django在X_CSRF的前面添加了HTTP_,所以实际传递的是就是X_CSRFtoken,而在前端页面的ajax传递的时候由于不能使用下划线所以传递的是X_CSRFtoken

    下面是在前端ajax中写的具体内容:

     1 $("#btn1").click(function () {
     2         $.ajax({
     3             url:"/login/",
     4             type:"POST",
     5             data:{"usr":"root","pwd":"123"},
     6             headers:{ "X-CSRFtoken":$.cookie("csrftoken")},
     7             success:function (arg) {
     8 
     9             }
    10         })
    11     })

    但是如果页面中有多个ajax请求的话就在每个ajax中添加headers信息,所以可以通过下面方式在所有的ajax中都添加

    1 $.ajaxSetup({
    2             beforeSend:function (xhr,settings) {
    3                 xhr.setRequestHeader("X-CSRFtoken",$.cookie("csrftoken"))
    4             }
    5         });

    这样就会在提交ajax之前执行这个方法,从而在所有的ajax里都加上这个csrftoken

    这里的xhr是XMLHttpRequest的简写,ajax调用的就是这个方法

    如果想要实现在当get方式的时候不需要提交csrftoken,当post的时候需要,实现这种效果的代码如下:

     1 function csrfSafeMethod(method) {
     2             // these HTTP methods do not require CSRF protection
     3             return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
     4         }
     5         $.ajaxSetup({
     6             beforeSend: function(xhr, settings) {
     7                 if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
     8                     xhr.setRequestHeader("X-CSRFToken", csrftoken);
     9                 }
    10             }
    11         });

    这样就实现了当GET|HEAD|OPTIONS|TRACE这些方式请求的时候不需要提交csrftoken

    总结

    1、csrf在ajax提交的时候通过请求头传递的给后台的

    2、csrf在前端的key为:X-CSRFtoken,到后端的时候django会自动添加HTTP_,并且最后为HTTP_X_CSRFtoken

    3、csrf在form中提交的时需要在前端form中添加{%csrftoken%}

  • 相关阅读:
    大数据入门,到底要怎么学习大数据?
    大数据
    将JWT与Spring Security OAuth结合使用
    使用OAuth保护REST API并使用简单的Angular客户端
    自定义Spring Security的身份验证失败处理
    Spring Security方法级别授权使用介绍
    Nginx高并发配置思路(轻松应对1万并发量)
    Spring Security 5中的默认密码编码器
    Spring Boot Security配置教程
    Spring Security在标准登录表单中添加一个额外的字段
  • 原文地址:https://www.cnblogs.com/garrett0220/p/9070617.html
Copyright © 2011-2022 走看看