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 url
from app01 import views
urlpatterns = [
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, render
def 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 res
else:
return redirect(f'/login/?target={target_path}')
return inner
def 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 res
return render(request, 'login.html')
@login_auth
def home(request):
return HttpResponse('这是home页面,只有登录的用户可以查看')
@login_auth
def book(request):
return HttpResponse('这是book页面,只有登录的用户可以查看')
# 退出登录
def logout(request):
res = redirect('/login/')
# 删掉cookie
res.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, render
def 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 inner
def 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'] = username
request.session['password'] = password
target = request.GET.get('next')
if target:
return redirect(target)
return redirect('/home/')
return render(request, 'login.html')
@login_auth
def home(request):
return HttpResponse('只有登录的用户能看到')
@login_auth
def book(request):
return HttpResponse('只有登录的用户能看到')
@login_auth
def logout(request):
request.session.flush()
return redirect('/login/')
CBV添加装饰器
方式一
直接在类中的某个方法上添加
from django import views
from django.utils.decorators import method_decorator
class 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)