Cookie、Session、Token的由来

我们知道HTTP协议无连接的, 也就是不保存用户的状态信息

早期(十几年前)的网页是静态的, 数据都是写死的, 人们访问网页只是用来查看新闻的, 没有保存用户状态的需求

而往后出现了像论坛、博客、网购这一类需要保存用户信息的网站, 如果网站不保存用户的状态信息, 意味着用户每次访问都需要重新输入用户名和密码, 这无疑对用户的体验是极其不好的

于是, 就出现了会话跟踪技术, 我们可以把它理解为客户端与服务端之间的一次会晤, 一次会晤包含的多次请求与响应, 每次请求都带着请求参数, 比如请求登入的请求参数是用户名和密码, 服务端就会拿着请求参数与数据库去比对, 找到相应的用户信息

如何实现会话跟踪 : 在HTTP协议中可以使用Cookie来完成, 在Web开发中可以使用Session来完成

  • Cookie是存在浏览器中的键值对, 每次发送请求都携带者参数, 但是容易被截获, 不安全
  • 于是就出现了Session, 它是存在于服务端的键值对, key为随机字符串, 安全性提高了, 但所有的数据都存在服务器中, 服务器的压力很大
  • 之后便产生了Token的概念, 服务端签发加密后的字符串给客户端浏览器保存, 客户端每次请求携带用户名和密码, 并加上由服务端签发的用户名和密码加密的字符串, 服务端收到请求后再对用户名密码加密, 与后面携带的密文对比, 由于它也是保存在客户端浏览器上的, 所以也叫Cookie

Cookie简介

什么是Cookie

  • Cookie是服务器保存在客户端浏览器之上的key-value键值对 : username='shawn';password="123"
  • 它是随着服务器的响应发送给客户端, 客户端将其保存, 下一次请求时会将Cookie放在其中, 服务器通过识别Cookie就能知道是哪个客户端浏览器

Cookie规范

  • Cookie大小上限为4KB
  • 一个服务器最多在客户端浏览器上保存20个Cookie
  • 一个浏览器最多保存300个Cookie

上面是HTTP中Cookie的规范, 现在浏览器的竞争, 有些Cookie大小能打到8KB, 最多可以保存500个Cookie

不同浏览器之间的Cookie是不共享的

安全性

  • Cookie保存在浏览器本地, 意味着很容易被窃取和篡改

Session简介

什么是Session

  • 存放在服务器上的键值对
  • Cookie可以保存状态, 但本身最大只能支持4069字节, 并且不安全, 于是就出现了Session
  • 它能支持更多字节, 并且保存在服务器上, 具有较高的安全性,Session基于Cookie, 本地存放服务器返回给浏览器的随机字符串
  • 客户端浏览器请求中携带随机字符串(session_id), 服务端收到后与数据库中存储的session做对比

Cookie与Session区别

  1. cookie是存在于浏览器上的, 保存形式以key:value键值对的形式
  2. session是存在于服务端的, django中保存在django_session表中, key: 对应session_key字段, value: 对应session_data, 还有一个session_date用来保存终止会话时间 (默认14天)
  3. session是基于cookie工作的, 在djangosession会告知浏览器以sessionid:随机字符的格式保存数据

Token简介

什么是Token

  • session数据保存在服务端, 提升了安全性, 但如果用户数据量特别多, 那么服务器的压力就会非常大
  • 所以不再在服务端中保存数据
  • Token采用了jwt(json web token)的认证方式, 数据格式分为三段式 :Header(头部)、Payload(负载)、Signature(签名)
  • 登入成功之后, 将第一段数据与第二段数据进行加密(加密算法是你后端开发自定义的), 加密后得到的字符串放在最后一段, 然后整体的返回给浏览器
  • 浏览器下次访问的时候就会带着该信息, 服务端收到再取前两段进行加密与第三段对比

Django中cookie的使用

关键字:res.set_cookie('key','value)

  1. def index(request):
  2. res = HttpResponse('生成了cookie')
  3. res.set_cookie('name', 'kevin')
  4. return res

关键字: request.COOKIE.get('key')

  1. def index(request):
  2. res = request.COOKIES.get('name')
  3. print(res)
  4. return HttpResponse('ok')

关键字: res.set_cookie('key','value')

  1. def index(request):
  2. res = HttpResponse('重新生成了cookie')
  3. res.set_cookie('name', 'kevin666')
  4. return res

关键字 : res.delete_cookie('key')

  1. def index(request):
  2. res = HttpResponse('删除了cookie')
  3. res.delete_cookie('name')
  4. return res

cookie加盐

  1. def index(request):
  2. res = HttpResponse('加盐生成cookie')
  3. # 加盐操作
  4. res.set_signed_cookie('name', 'kevin', salt='加点盐',max_age=3) # max_age 超时时间:默认是秒数
  5. return res

Django中Session的使用

关键字:request.session['key'] = value

  1. def index(request):
  2. request.session['name'] = 'kevin'
  3. return HttpResponse('设置了一个session')
  4. # session信息保存在了自带的 django_session 表

删(客户端)

关键字:request.session.delete()

  1. def index(request):
  2. request.session.delete()
  3. return HttpResponse('客户端删除成功')

删(服务端、客户端)

关键字:request.session.flush()

  1. def index(request):
  2. request.session.flush()
  3. return HttpResponse('客户端、服务端删除成功')

关键字:request.session['key'] = value

  1. def index(request):
  2. name1 = request.session.get('name')
  3. request.session['name'] = 'kevin666'
  4. name2 = request.session.get('name')
  5. return HttpResponse(f'修改前{name1},修改后{name2}')

关键字:request.session.session_key

  1. def index(request):
  2. res1 = request.session.get('name')
  3. res2 = request.session.session_key # 获取产生的随机字符串
  4. print(res1, res2)
  5. return HttpResponse('获取session')

补充

request.session.set_expiry(value) 设置超时时间

  • 如果value是个整数,session会在些秒数后失效
  • 如果value是个datatimetimedeltasession就会在这个时间后失效
  • 如果value是0,用户关闭浏览器session就会失效
  • 如果valueNone,session会依赖全局session失效策略

基于cookie的登入认证示例

urls.py

  1. from django.conf.urls import url
  2. from app01 import views
  3. urlpatterns = [
  4. url(r'^login/', views.login),
  5. url(r'^home/', views.home),
  6. url(r'^book/', views.book),
  7. url(r'^logout/', views.logout),
  8. ]

views.py

  1. from django.shortcuts import redirect, HttpResponse, render
  2. def login_auth(func_name):
  3. def inner(request, *args, **kwargs):
  4. # 获取用户没有登录之前想要访问的网址地址
  5. target_path = request.get_full_path()
  6. if request.COOKIES.get('name') == 'kevin':
  7. res = func_name(request, *args, **kwargs)
  8. return res
  9. else:
  10. return redirect(f'/login/?target={target_path}')
  11. return inner
  12. def login(request):
  13. # 判断是否登录过(获取cookie)
  14. if request.COOKIES.get('name') == 'kevin':
  15. return redirect('/home/')
  16. if request.method == 'POST':
  17. username = request.POST.get('username')
  18. password = request.POST.get('password')
  19. if username == 'kevin' and password == '123':
  20. # 获取用户之前想访问的地址,登录跳转
  21. target_path = request.GET.get('target')
  22. if target_path:
  23. res = redirect(f'{target_path}')
  24. else:
  25. # 默认登录之后跳转的页面
  26. res = redirect('/home/')
  27. # 登录成功cookie保存到浏览器
  28. res.set_cookie('name', 'kevin')
  29. return res
  30. return render(request, 'login.html')
  31. @login_auth
  32. def home(request):
  33. return HttpResponse('这是home页面,只有登录的用户可以查看')
  34. @login_auth
  35. def book(request):
  36. return HttpResponse('这是book页面,只有登录的用户可以查看')
  37. # 退出登录
  38. def logout(request):
  39. res = redirect('/login/')
  40. # 删掉cookie
  41. res.delete_cookie('name')
  42. return res

login.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
  8. <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
  9. </head>
  10. <body>
  11. <div class="container">
  12. <div class="row">
  13. <div class="col-lg-6 col-md-offset-3" style="max-width: 300px">
  14. <h3 class="text-center">登录</h3>
  15. <form action="" method="post">
  16. <p>username:
  17. <input type="text" class="form-control" name="username">
  18. </p>
  19. <p>password:
  20. <input type="password" class="form-control" name="password">
  21. </p>
  22. <input type="submit" class="btn btn-success btn-block ">
  23. </form>
  24. </div>
  25. </div>
  26. </div>
  27. </body>
  28. </html>

基于session的登入认证示例

  1. from django.shortcuts import redirect, HttpResponse, render
  2. def login_auth(func_name):
  3. def inner(request, *args, **kwargs):
  4. target_path = request.get_full_path()
  5. if request.session.get('name') == 'kevin':
  6. return func_name(request, *args, **kwargs)
  7. return redirect(f'/login/?next={target_path}')
  8. return inner
  9. def login(request):
  10. if request.session.get('name') == 'kevin':
  11. return redirect('/home/')
  12. if request.method == 'POST':
  13. username = request.POST.get('username')
  14. password = request.POST.get('password')
  15. if username == 'kevin' and password == '123':
  16. request.session['name'] = username
  17. request.session['password'] = password
  18. target = request.GET.get('next')
  19. if target:
  20. return redirect(target)
  21. return redirect('/home/')
  22. return render(request, 'login.html')
  23. @login_auth
  24. def home(request):
  25. return HttpResponse('只有登录的用户能看到')
  26. @login_auth
  27. def book(request):
  28. return HttpResponse('只有登录的用户能看到')
  29. @login_auth
  30. def logout(request):
  31. request.session.flush()
  32. return redirect('/login/')

CBV添加装饰器

方式一

直接在类中的某个方法上添加

  1. from django import views
  2. from django.utils.decorators import method_decorator
  3. class MyLoginView(views.View):
  4. @method_decorator(login_auth)
  5. def get(self, request):
  6. return HttpResponse('from CBV get view')
  7. def post(self, request):
  8. return HttpResponse('from CBV post view')

方式二

直接在类名上添加并指定

  1. @method_decorator(login_auth, name='get') # 如果get方法和post方法都需要校验的话就写两个装饰器
  2. class MyLoginView(views.View):
  3. def get(self, request):
  4. return HttpResponse('from CBV get view')
  5. def post(self, request):
  6. return HttpResponse('from CBV post view')

方式三

重写dispatch方`法并添加作用于类中所有的方法

  1. class MyLoginView(views.View):
  2. def get(self, request):
  3. return HttpResponse('from CBV get view')
  4. def post(self, request):
  5. return HttpResponse('from CBV post view')
  6. @method_decorator(login_auth)
  7. def dispatch(self, request, *args, **kwargs):
  8. return super().dispatch(request, *args, **kwargs)