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%}

原文地址:https://www.cnblogs.com/garrett0220/p/9070617.html