一、什么是Cookie

1.什么是Cookie?

Cookie是保存在客户端浏览器中的文件,其中记录了服务器让浏览器记录的一些键值对(类似字典)。

当Cookie中存在数据时,浏览器在访问网站时会读取属于自己的数据,并携带在请求中发送给服务器。

这种机制可以用于许多场景,例如用户登录。

Cookie非常重要,如果禁用了Cookie,大部分网站都不好用。

2.如何禁用Cookie

以Chrome为例:

进入设置--->高级--->隐私设置与安全性--->网站设置(或内容设置)--->Cookie和网站数据--->允许网站保存和读取Cookie数据(关闭)

3.关闭Cookie尝试登陆JD

在没有Cookie的情况下,会被提示"请再次登陆"或者"请您启用浏览器Cookie功能或更换浏览器"。

二、基于Cookie实现用户登录

1.实现登录页面mlogin.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Mgmt Login</title>
  6. <link rel="stylesheet" href="/static/commons.css"/>
  7. <style>
  8. label{
  9. width: 80px;
  10. text-align: right;
  11. display: inline-block;
  12. }
  13. .error_span{
  14. color: red;
  15. }
  16. </style>
  17. </head>
  18. <body class="common">
  19. <form action="/mgmt/login/" method="post">
  20. <p>
  21. <label for="username">用户名:</label>
  22. <input id="username" type="text" name="user"/>
  23. <span class="error_span">{{ user_error }}</span>
  24. </p>
  25. <p>
  26. <label for="password">密码:</label>
  27. <input id="password" type="password" name="pwd"/>
  28. <input type="submit" value="提交"/>
  29. <span class="error_span">{{ pwd_error }}</span>
  30. </p>
  31. </form>
  32. <script src="/static/jquery-1.12.4.js"></script>
  33. </body>
  34. </html>

2.实现视图函数login()

  1. def login(request):
  2. pwd_error_msg = ''
  3. # 当用户使用GET请求时,让其登录
  4. if request.method == "GET":
  5. return render(request, 'mlogin.html')
  6. if request.method == 'POST':
  7. username = request.POST.get('user', None)
  8. password = request.POST.get('pwd', None)
  9. dic = user_info.get(username)
  10. if not dic:
  11. # 用户不存在,重新登录
  12. pwd_error_msg = "用户不存在"
  13. return render(request, 'mlogin.html', {"pwd_error": pwd_error_msg})
  14. if dic['pwd'] == password:
  15. response = redirect('/mgmt/host')
  16. response.set_cookie('username', username)
  17. return response
  18. else:
  19. # 密码不正确,重新登录
  20. pwd_error_msg = "密码不正确"
  21. return render(request, 'mlogin.html', {"pwd_error": pwd_error_msg})

当账号密码验证成功时,向客户端写入cookie,并跳转到/mgmt/host页面。

3.定义映射关系

  1. from django.contrib import admin
  2. from django.urls import path
  3. from django.urls import re_path
  4.  
  5. from mgmt import views
  6.  
  7. app_name = 'mgmt'
  8. urlpatterns = [
  9. path('index/', views.index),
  10. re_path('host', views.host),
  11. re_path('login', views.login),
  12. ]

4.在host()视图函数中验证cookie

  1. def host(request):
  2. # 如果是get请求,则将数据库中查询到的host列表和业务线列表返回,展示在页面上
  3. if request.method == 'GET':
  4. username = request.COOKIES.get('username')
  5. if not username:
  6. return redirect('/mgmt/login')
  7. host_list = models.Host.objects.all()
  8. busi_list = models.Business.objects.all()
  9. return render(request, 'host.html', {'username': username, 'host_list': host_list, 'busi_list': busi_list})
  10. elif request.method == 'POST': # 当用户使用模态框添加主机时,使用表单POST提交
  11. # 获取表单提交的数据
  12. host = request.POST.get('hostname')
  13. ip = request.POST.get('ip')
  14. port = request.POST.get('port')
  15. # 这里的busi获取到的是select对应的busi_id
  16. busi = request.POST.get('busi_id')
  17. # 插入数据库
  18. models.Host.objects.create(
  19. hostname=host,
  20. ip=ip,
  21. port=port,
  22. busi_id=busi
  23. )
  24. # 重定向到host页面,以GET重新请求,页面就可以显示新的值
  25. return redirect('/mgmt/host')

如果没有cookie,则说明没有登录,跳转回login页面。如果有cookie,则显示登录用户名。

/mgmt/host页面的html代码和models代码参考:[Python自学] day-20 (Django-ORM、Ajax)

三、Cookie参数

1.Cookie失效时间

我们如果使用set_cookie('key',value)来设置Cookie,则Cookie会在关闭浏览器时失效。

有两种方式设置失效时间:

1)max_age参数(单位秒)

  1. set_cookie('username', username, max_age=10)

这样设置以后,Cookie的有效时间就是10s,10s后刷新页面,就会跳转到登录页面(前提是写了这个跳转逻辑)。

2)expires参数(参数为datetime)

  1. import datetime
  2. now = datetime.datetime.utcnow()
  3. delay = datetime.timedelta(10)
  4. response.set_cookie('username', username, expires= now+delay)

同样设置以后,有效时间也是10s。

2.Cookie path、domain、secure、httponly参数

path参数:

当我们按前面章节的代码来添加Cookie的时候,Cookie是在所有页面都能访问的。

但是我们有可能需要在不同的页面访问不同的Cookie值,例如论坛的两个页面,一个页面(host页面)设置一页显示50条数据,另外一个页面(app)设置一页显示20条数据。

使用path参数:

  1. set_cookie('show_num', 50, path='/') # 所有页面
  2. set_cookie('show_num', 50, path='/host') # Host页面一页显示50条数据
  3. set_cookie('show_num', 20, path='/app') # App页面一页显示20条数据

domain参数:

用于设置cookie生效的域名。用于过滤生效范围。

secure参数:

如果使用的是https协议,则secure必须设置为Ture。

httponly参数:

如果httponly=True,则表示Cookie只能通过http协议传输,无法被JavaScript获取(不是绝对的,底层抓包可以获取到也可以被覆盖)

如果不设置,则在前端JS代码中可以使用以下代码获取cookie:

  1. console.log(document.cookie) //"username=leokale"

四、Cookie实现动态调整每页显示条目数

在  [Python自学] day-21 (1) (请求信息、html模板继承与导入、自定义模板函数、自定义分页) 中,我们实现了分页功能。效果如下:

每页默认显示10条数据。我们可以使用Cookie来实现动态调整每页显示条目数量。

1.在html中添加一个<select>标签

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Paging</title>
  6. <style>
  7. /*分别定义页签按钮的效果,以及当前页面的页签按钮效果*/
  8. .pagination{
  9. font-size: 12px;
  10. /*一般页签按钮都居中*/
  11. /*text-align: center;*/
  12. }
  13. .pagination .page_num{
  14. display: inline-block;
  15. padding: 2px 5px;
  16. border: 1px solid #9aafe5;
  17. color: #2e6ab1;
  18. margin: 0 2px;
  19. text-decoration: none;
  20. }
  21. .pagination .page_num.active{
  22. background-color: #2e6ab1;
  23. border: 1px solid #000080;
  24. color: #fff;
  25. font-weight: bold;
  26. margin: 0 2px;
  27. padding: 2px 5px;
  28. }
  29. a{
  30. outline: 0;
  31. }
  32. </style>
  33. </head>
  34. <body>
  35. <!-- 显示内容 -->
  36. <ul>
  37. {% for text in contents %}
  38. <li>
  39. {{ text }}
  40. </li>
  41. {% endfor %}
  42. </ul>
  43. <select id="select_page_show_num"onchange="changePageShowNum(this);">
  44. <option value="10">10</option>
  45. <option value="30">30</option>
  46. <option value="50">50</option>
  47. <option value="100">100</option>
  48. </select>
  49. <!-- 显示页签按钮 -->
  50. <div class="pagination">
  51. {{ paginations }}
  52. </div>
  53. <script src="/static/jquery-1.12.4.js"></script>
  54. <script src="/static/jquery.cookie.js"></script>
  55. <script>
  56. // 页面加载完后,马上读取cookie的值,设置在select框中
  57. $(function(){
  58. var v = $.cookie('per_page_count');
  59. if(!v){
  60. v = '';
  61. }
  62. $("#select_page_show_num").val(v)
  63. });
  64. //当用户选择select标签中的值后,设置cookie
  65. function changePageShowNum(ths){
  66. var v = $(ths).val();
  67. $.cookie('per_page_count',v);
  68. location.reload();
  69. };
  70. </script>
  71. </body>
  72. </html>

解释:

1)导入jquery cookie插件,为jquery添加便利操作cookie的功能。下载地址 https://plugins.jquery.com/cookie/

2)添加一个<select>标签,提供用户每页显示数目的功能。

3)当页面加载完毕的时候,读取cookie是否存在per_page_count的值,如果没有,则默认为10,如果有,则设置在select标签中,确保显示与cookie一致。

4)当用户改变<select>的选项时,动态的将值写入per_page_count中。

2.修改paging()视图函数

  1. def paging(request, pnum):
  2. # 从请求中,获取Cookie per_page_count的值
  3. ppc = request.COOKIES.get('per_page_count')
  4. # 将值转换为int类型,并将其作为参数传入Paging构造函数
  5. ppc = int(ppc)
  6. # 获取一个分页对象
  7. pager_obj = Paging(pnum, len(PAGES), per_page_count=ppc)
  8. # 获取页签按钮的list
  9. paginations = pager_obj.pager_str('/mgmt/paging/')
  10. # 根据start和end来获取数据,对应数据库的行数
  11. contents = PAGES[pager_obj.start_idx: pager_obj.end_idx]
  12.  
  13. return render(request, 'paging.html', {"contents": contents, "paginations": paginations})

3.实现效果

五、带签名的Cookie(加密)

普通设置的Cookie都是明文的,如果我们想对cookie进行加密,则使用以下方式:

  1. set_signed_cookie('username', username, salt='nsakjdf98s7dfhsf')
  2. get_signed_cookie('username', salt='nsakjdf98s7dfhsf')

通过使用加盐的方式来加密,salt在设置cookie和获取cookie的时候一定要对应上

六、使用装饰器实现用户验证

1.FBV装饰器

如果我们在很多页面的视图函数都要验证用户是否登录(验证是否存在指定Cookie),则需要在每个视图函数中写以下代码:

  1. username = request.COOKIES.get('username')
  2. if not username:
  3. return redirect('/mgmt/login')

如果使用装饰器,则实现为:

  1. # 实现用户登录cookie验证的装饰器
  2. def auth(func):
  3. def inner(request, *args, **kwargs):
  4. username = request.COOKIES.get('username')
  5. if not username:
  6. return redirect('/mgmt/login')
  7. return func(request, *args, **kwargs)
  8.  
  9. return inner

2.CBV装饰器

当使用的是CBV的请求处理方式,里面的get、post方法都是分开的,如果我们为每一个函数都添加auth装饰器,比较麻烦。

django为我们提供了一个封装好的装饰器容器。

使用方法一(麻烦,不推荐):为每一个函数加上装饰器

  1. from django import views
  2. from django.utils.decorators import method_decorator
  3.  
  4. # 实现用户登录cookie验证的装饰器
  5. def auth(func):
  6. def inner(request, *args, **kwargs):
  7. username = request.COOKIES.get('username')
  8. if not username:
  9. return redirect('/mgmt/login')
  10. return func(request, *args, **kwargs)
  11.  
  12. return inner
  13.  
  14. class Host(views.View):
  15. @method_decorator(auth)
  16. def get(self, request):
  17. username = request.COOKIES.get('username')
  18. # 如果是get请求,则将数据库中查询到的host列表和业务线列表返回,展示在页面上
  19. if request.method == 'GET':
  20. host_list = models.Host.objects.all()
  21. busi_list = models.Business.objects.all()
  22. return render(request, 'host.html', {'username': username, 'host_list': host_list, 'busi_list': busi_list})
  23.  
  24. @method_decorator(auth)
  25. def post(self, request):
  26. # 获取表单提交的数据
  27. host = request.POST.get('hostname')
  28. ip = request.POST.get('ip')
  29. port = request.POST.get('port')
  30. # 这里的busi获取到的是select对应的busi_id
  31. busi = request.POST.get('busi_id')
  32. # 插入数据库
  33. models.Host.objects.create(
  34. hostname=host,
  35. ip=ip,
  36. port=port,
  37. busi_id=busi
  38. )
  39. # 重定向到host页面,以GET重新请求,页面就可以显示新的值
  40. return redirect('/mgmt/host')

使用方法二(麻烦):利用dispatch方法先于get、post方法执行。

  1. from django import views
  2. from django.utils.decorators import method_decorator
  3.  
  4. # 实现用户登录cookie验证的装饰器
  5. def auth(func):
  6. #......
  7.  
  8. class Host(views.View):
  9. @method_decorator(auth)
  10. def dispatch(self, request, *args, **kwargs):
  11. return super(Host, self).dispatch(request, *args, **kwargs)
  12.  
  13. def get(self, request):
  14. #......
  15.  
  16. def post(self, request):
  17. #......

由于dispatch方法先于get、post方法执行,所以在这里统一做验证就可以。

使用方法三(推荐):在类上加装饰器。

  1. from django import views
  2. from django.utils.decorators import method_decorator
  3.  
  4. # 实现用户登录cookie验证的装饰器
  5. def auth(func):
  6. #......
  7.  
  8. @method_decorator(auth, name='dispatch')
  9. class Host(views.View):
  10. def get(self, request):
  11. #......
  12.  
  13. def post(self, request):
  14. #......

在类前面加装饰器,并使用name参数指定成员方法名,相当于第二种使用方法。

[Python自学] day-21 (2) (Cookie、FBV|CBV装饰器)的更多相关文章

  1. [oldboy-django][2深入django]FBV + CBV + 装饰器

    FBV django CBV & FBV - FBV function basic view a. urls 设置 urls(r'^test.html$', views.test) b. vi ...

  2. FBV和CBV装饰器

    FBV装饰器: def cook(request): err_msg="" if request.method == "GET": return render( ...

  3. django基础 -- 4. 模板语言 过滤器 模板继承 FBV 和CBV 装饰器 组件

    一.语法 两种特殊符号(语法): {{ }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}. 二.变量 1. 可直接用  {{ 变量名 }} (可调用字符串, 数字 ,列表,字典,对象等) ...

  4. Django基础七之CBV装饰器和中间件

    Django基础七之CBV装饰器和中间件 目录 Django基础七之CBV装饰器和中间件 1. CBV加装饰器 2. Django中间件 2.1 Django中间件介绍 2.2 自定义中间件 2.2. ...

  5. Python自动化面试必备 之 你真明白装饰器么?

    Python自动化面试必备 之 你真明白装饰器么? 装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多小白来讲,这个功能 有点绕 ...

  6. django CBV装饰器 自定义django中间件 csrf跨站请求伪造 auth认证模块

    CBV加装饰器 第一种 @method_decorator(装饰器) 加在get上 第二种 @method_decorator(login_auth,name='get') 加在类上 第三种 @met ...

  7. Python自动化运维之6、函数装饰器

    装饰器: 装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大.装饰器一般接受一个函数对象作为参数, ...

  8. python中“生成器”、“迭代器”、“闭包”、“装饰器”的深入理解

    python中"生成器"."迭代器"."闭包"."装饰器"的深入理解 一.生成器 1.生成器定义:在python中,一边 ...

  9. python语法生成器、迭代器、闭包、装饰器总结

    1.生成器 生成器的创建方法: (1)通过列表生成式创建 可以通过将列表生成式的[]改成() eg: # 列表生成式 L = [ x*2 for x in range(5)] # L = [0, 2, ...

随机推荐

  1. LUA的table实现

    数据结构 下面的结构体是lua中所定义的table typedef struct Table { CommonHeader; lu_byte flags; /* 1<<p means ta ...

  2. cdoj 574 High-level ancients dfs序+线段树 每个点所加权值不同

    High-level ancients Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/s ...

  3. # OpenGL常用函数详解(持续更新)

    OpenGL常用函数详解(持续更新) 初始化 void glutInit(int* argc,char** argv)初始化GULT库,对应main函数的两个参数 void gultInitWindo ...

  4. python基础(十)--函数进阶

    嵌套函数 >>> graphic = '三角形' >>> def chang(): graphic = '正方形' def chang1(): #内部嵌套的函数命名 ...

  5. pthread_cond_t

    条件锁pthread_cond_t (1)pthread_cond_wait的使用 等待线程1. 使用pthread_cond_wait前要先加锁2. pthread_cond_wait内部会解锁,然 ...

  6. java 缓存

    外存: 也就是我们经常说的(CDEF盘的大小)外储存器是指除计算机内存及CPU缓存以外的储存器,此类储存器一般断电后仍然能保存数据.常见的外存储器有硬盘.软盘.光盘.U盘等,一般的软件都是安装在外存中 ...

  7. DaemonSet和StatefulSet

    DaemonSet 的使用 通过该控制器的名称我们可以看出它的用法:Daemon,就是用来部署守护进程的,DaemonSet用于在每个Kubernetes节点中将守护进程的副本作为后台进程运行,说白了 ...

  8. 按Excel的模板导出数据

    思路:把Excel模板上传到服务器:然后读取文档,写入数据,另存为新的文件 然后就是导出的时候处理数据定义的字符串,按数据导出:注意读取的数据流要处理下,不然会报异常 public Stream Ge ...

  9. 【css】display:flex和display:box有什么区别

    说法一: 注意:前者是flex 2012年的语法,也将是以后标准的语法,大部分浏览器已经实现了无前缀版本.后者是2009年的语法,已经过时,是需要加上对应前缀的.所以兼容性的代码,大致如下displa ...

  10. TCP面向字节流和UDP面向报文的区别

    TCP面向字节流 打个比方比喻TCP,你家里有个蓄水池,你可以里面倒水,蓄水池上有个龙头,你可以通过龙头将水池里的水放出来,然后用各种各样的容器装(杯子.矿泉水瓶.锅碗瓢盆)接水. 上面的例子中,往水 ...