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区别
cookie是存在于浏览器上的, 保存形式以key:value键值对的形式session是存在于服务端的,django中保存在django_session表中, key: 对应session_key字段, value: 对应session_data, 还有一个session_date用来保存终止会话时间 (默认14天)session是基于cookie工作的, 在django中session会告知浏览器以sessionid:随机字符的格式保存数据
Token简介
什么是Token
- session数据保存在服务端, 提升了安全性, 但如果用户数据量特别多, 那么服务器的压力就会非常大
- 所以不再在服务端中保存数据
- Token采用了jwt(json web token)的认证方式, 数据格式分为三段式 :Header(头部)、Payload(负载)、Signature(签名)
- 登入成功之后, 将第一段数据与第二段数据进行加密(加密算法是你后端开发自定义的), 加密后得到的字符串放在最后一段, 然后整体的返回给浏览器
- 浏览器下次访问的时候就会带着该信息, 服务端收到再取前两段进行加密与第三段对比
Django中cookie的使用
增
关键字:res.set_cookie('key','value)
def index(request):res = HttpResponse('生成了cookie')res.set_cookie('name', 'kevin')return res
查
关键字: request.COOKIE.get('key')
def index(request):res = request.COOKIES.get('name')print(res)return HttpResponse('ok')
改
关键字: res.set_cookie('key','value')
def index(request):res = HttpResponse('重新生成了cookie')res.set_cookie('name', 'kevin666')return res
删
关键字 : res.delete_cookie('key')
def index(request):res = HttpResponse('删除了cookie')res.delete_cookie('name')return res
cookie加盐
def index(request):res = HttpResponse('加盐生成cookie')# 加盐操作res.set_signed_cookie('name', 'kevin', salt='加点盐',max_age=3) # max_age 超时时间:默认是秒数return res
Django中Session的使用
增
关键字:request.session['key'] = value
def index(request):request.session['name'] = 'kevin'return HttpResponse('设置了一个session')# session信息保存在了自带的 django_session 表
删(客户端)
关键字:request.session.delete()
def index(request):request.session.delete()return HttpResponse('客户端删除成功')
删(服务端、客户端)
关键字:request.session.flush()
def index(request):request.session.flush()return HttpResponse('客户端、服务端删除成功')
改
关键字:request.session['key'] = value
def index(request):name1 = request.session.get('name')request.session['name'] = 'kevin666'name2 = request.session.get('name')return HttpResponse(f'修改前{name1},修改后{name2}')
查
关键字:request.session.session_key
def index(request):res1 = request.session.get('name')res2 = request.session.session_key # 获取产生的随机字符串print(res1, res2)return HttpResponse('获取session')
补充
request.session.set_expiry(value) 设置超时时间
- 如果
value是个整数,session会在些秒数后失效 - 如果
value是个datatime或timedelta,session就会在这个时间后失效 - 如果
value是0,用户关闭浏览器session就会失效 - 如果
value是None,session会依赖全局session失效策略
基于cookie的登入认证示例
urls.py
from django.conf.urls import urlfrom app01 import viewsurlpatterns = [url(r'^login/', views.login),url(r'^home/', views.home),url(r'^book/', views.book),url(r'^logout/', views.logout),]
views.py
from django.shortcuts import redirect, HttpResponse, renderdef login_auth(func_name):def inner(request, *args, **kwargs):# 获取用户没有登录之前想要访问的网址地址target_path = request.get_full_path()if request.COOKIES.get('name') == 'kevin':res = func_name(request, *args, **kwargs)return reselse:return redirect(f'/login/?target={target_path}')return innerdef login(request):# 判断是否登录过(获取cookie)if request.COOKIES.get('name') == 'kevin':return redirect('/home/')if request.method == 'POST':username = request.POST.get('username')password = request.POST.get('password')if username == 'kevin' and password == '123':# 获取用户之前想访问的地址,登录跳转target_path = request.GET.get('target')if target_path:res = redirect(f'{target_path}')else:# 默认登录之后跳转的页面res = redirect('/home/')# 登录成功cookie保存到浏览器res.set_cookie('name', 'kevin')return resreturn render(request, 'login.html')@login_authdef home(request):return HttpResponse('这是home页面,只有登录的用户可以查看')@login_authdef book(request):return HttpResponse('这是book页面,只有登录的用户可以查看')# 退出登录def logout(request):res = redirect('/login/')# 删掉cookieres.delete_cookie('name')return res
login.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1"><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head><body><div class="container"><div class="row"><div class="col-lg-6 col-md-offset-3" style="max-width: 300px"><h3 class="text-center">登录</h3><form action="" method="post"><p>username:<input type="text" class="form-control" name="username"></p><p>password:<input type="password" class="form-control" name="password"></p><input type="submit" class="btn btn-success btn-block "></form></div></div></div></body></html>
基于session的登入认证示例
from django.shortcuts import redirect, HttpResponse, renderdef login_auth(func_name):def inner(request, *args, **kwargs):target_path = request.get_full_path()if request.session.get('name') == 'kevin':return func_name(request, *args, **kwargs)return redirect(f'/login/?next={target_path}')return innerdef login(request):if request.session.get('name') == 'kevin':return redirect('/home/')if request.method == 'POST':username = request.POST.get('username')password = request.POST.get('password')if username == 'kevin' and password == '123':request.session['name'] = usernamerequest.session['password'] = passwordtarget = request.GET.get('next')if target:return redirect(target)return redirect('/home/')return render(request, 'login.html')@login_authdef home(request):return HttpResponse('只有登录的用户能看到')@login_authdef book(request):return HttpResponse('只有登录的用户能看到')@login_authdef logout(request):request.session.flush()return redirect('/login/')
CBV添加装饰器
方式一
直接在类中的某个方法上添加
from django import viewsfrom django.utils.decorators import method_decoratorclass MyLoginView(views.View):@method_decorator(login_auth)def get(self, request):return HttpResponse('from CBV get view')def post(self, request):return HttpResponse('from CBV post view')
方式二
直接在类名上添加并指定
@method_decorator(login_auth, name='get') # 如果get方法和post方法都需要校验的话就写两个装饰器class MyLoginView(views.View):def get(self, request):return HttpResponse('from CBV get view')def post(self, request):return HttpResponse('from CBV post view')
方式三
重写dispatch方`法并添加作用于类中所有的方法
class MyLoginView(views.View):def get(self, request):return HttpResponse('from CBV get view')def post(self, request):return HttpResponse('from CBV post view')@method_decorator(login_auth)def dispatch(self, request, *args, **kwargs):return super().dispatch(request, *args, **kwargs)
