赞
踩
参考博文地址:https://www.cnblogs.com/liuqingzheng/p/8990027.html
1.coookie,session,token发展史
- 无cookie时期
有一段时间Eeb基本上就是文档的浏览时期,既然是浏览,作为服务器,不需要记录某人在某段时间都浏览了什么文档,每次的请求都是一个新的HTTP协议,就是请求加响应,不需要记住是谁发了HTTP请求,每个请求对于“我”来说都是全新的。- cookie+session认证时期
随着交互式Web应用的兴起,一些在线购物网站,需要登录的网站即需要面临一个管理会话的问题。这些网站必须要记住哪些人登录系统,哪些人购买商品或者各种功能实现,也就是说我们必须把每个人与他在该网站所做的行为区分开,这形成了新的挑战,因为HTTP请求是无状态的。
所以想出的办法就是给大家发一个会话标识(session id),即发送一个随机字符串,每个人的随机字符串不同,每次该网站收到发送来的HTTP请求时,连同该字符串一起发送过来,这样就对每个人起到区分的作用。- cookie+session存在问题即解决
接下来面临的新问题是服务器很难过,表示本宝宝不服,每个人只需要保存自己的(session id),可本宝宝要保存你们所有人的session id,这可不是一个小数目。
所以,这对服务器来说变成了一个巨大的开销,严重的限制了服务器扩展能力。比如说我用两个机器组成了一个集群,小F通过通过机器A登录了系统,那session id 会保存在机器A上,那假设小F的下一次请求被转到机器B上怎么办?机器B上并没有存小F的session id。
有时候会采用一点小伎俩: session sticky , 就是让小F的请求一直粘连在机器A上, 但是这也不管用, 要是机器A挂掉了, 还得转到机器B去。
那只好做session 的复制了, 把session id 在两个机器之间搬来搬去, 快累死了。
后来有个叫Memcached的支了招: 把session id 集中存储到一个地方, 所有的机器都来访问这个地方的数据, 这样一来,就不用复制了, 但是增加了单点失败的可能性, 要是那个负责session 的机器挂了, 所有人都得重新登录一遍, 估计得被人骂死。
也尝试把这个单点的机器也搞出集群,增加可靠性, 但不管如何, 这小小的session 对我来说是一个沉重的负担。- Token认证
于是人们开始思考让客户端去保存session。
可是服务端不保存这些session id的话,怎么验证客户端发给我的session id的却是我生成的呢? 如果不去验证,我们都不知道他们是不是合法登录的用户, 那些不怀好意的家伙们就可以伪造session id , 为所欲为了。
这里即提出关键字为验证!!!
比如说, 小F已经登录了系统, 我给他发一个令牌(token), 里边包含了小F的 user id, 下一次小F 再次通过Http 请求访问我的时候, 把这个token 通过Http header 带过来不就可以了。
不过这和session id没有本质区别,于是提出对数据做一个签名,比如说我用HMAC-SHA256 算法,加上一个只有我才知道的密钥, 对数据做一个签名, 把这个签名和数据一起作为token , 由于密钥别人不知道, 就无法伪造token了。
这个token 我不保存, 当小F把这个token 给我发过来的时候,我再用同样的HMAC-SHA256 算法和同样的密钥,对数据再计算一次签名, 和token 中的签名做个比较, 如果相同, 我就知道小F已经登录过了,并且可以直接取到小F的user id , 如果不相同, 数据部分肯定被人篡改过, 我就告诉发送者: 对不起,没有认证。
Token 中的数据是明文保存的(虽然我会用Base64做下编码, 但那不是加密), 还是可以被别人看到的, 所以我不能在其中保存像密码这样的敏感信息。
当然,如果一个人token被别人偷走并发送给我,我也自能默认其为合法用。
故我们就不需要存session id,只要生成token即可。
2.Cookie,Session解释
Cookie
cookie 是一个非常具体的东西,指的就是浏览器里面能永久存储的一种数据,仅仅是浏览器实现的一种数据存储功能。
cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。Session
session 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。
session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。
服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。cookie和session的区别
session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高
获取session里的信息是通过存放在会话cookie里的session id获取的。而session是存放在服务器的内存中里,所以session里的数据不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里。
session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中
当浏览器关闭的时候会话cookie消失,所以sessionid也就消失了,但是session的信息还存在服务器端,只是查不到所谓的session,但它并不是不存在
3.token解释
- token三段式:
第一段:头:公司信息,加密方式等{}
第二段:荷载:真正的数据{name:jason,id:1}
第三段:签名:通过第一段和第二段,通过某种加密方式加密得到的- token的使用分两个阶段
登录成功后【签发token阶段】------》此阶段生成token三段式;
登录成功访问某个接口的【验证阶段】------》验证token是否合法- 三者区别
cookie是存在客户端浏览器的键值对
session是存在于服务端的键值对
token是三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端:存在移动端中)
使用token的认证机制,服务端还要存数据吗? token是服务的生成,客户端保存,服务端不存储token。
1.概念
Json web token (JWT), token的应用于web方向的称之为jwt
核心:
签发:登录接口签发;
认证:认证类认证
2.构成和工作原理
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
我们把它拆分一下:
- header:头
(1)声明类型,这里是jwt
(2)声明加密的算法:通常直接使用HMAC SHA256
(3)举个栗子:公司信息
由{
‘type’:‘JWT’,
‘alg’:‘HS256’
}
变成了(base64的编码)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9- payload:荷载
(1)exp:jwt的过期时间,这个时期时间必须要大于签发时间
(2)lat:jwt的签发时间
(3)举个栗子:用户信息
由{
“exp”: “1234567890”,
“name”: “John Doe”,
“userid”: 3
}
变成了(base64的编码)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9- signature:签名
把头和荷载加密后得到的:
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
3/基于session的认证机制
4.基于jwt的认证
5.注意点:
secret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,他就是你服务器的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt了。
1.概念
- base64 可以把字符串编码成base64的编码格式:(大小写字母,数字和 =)
eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9- base64可以把base64编码的字符串,解码回原来的格式
2.应用场景
- jwt中使用
- 网络中传输字符串就可以使用base64编码
- 网络中传输图片,也可能使用base64的编码
3.代码操作
需导入内置base64模块import base64 import json d = {'name':'lqz','userid':6,'age':19} info = json.dumps(d) print(info) # {"name": "lqz", "userid": 6, "age": 19} # 把字符串使用base64编码 res = base64.b64encode(info.encode('utf-8')) print(res) # >eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9 # 使用base64解码过程 res = base64.b64decode('eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9') print(res) # {"name": "lqz", "userid": 6, "age": 19}
'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
运行
1.jwt
签发:登录接口
认证:认证类
2.django中使用jwt
- 可以自己写
https://github.com/jpadilla/django-rest-framework-jwt (比较老) https://github.com/jazzband/djangorestframework-simplejwt (比较新)
- 1
- 2
- 3
- python第三方模块(djangorestframework-jwt)
(1)安装:pip3 install djangorestframework-jwt
(2)快速使用:
迁移表:因为它,默认使用auth的user表签发token;
创建超级用户:在auth的user表中要有记录;
不需要在写接口,如果是使用auth的user表作为用户表,它可以实现快速签发;
签发(登录):只需要在路由中配置(因为该第三方模块已经帮我们写好登录接口了)from django.contrib import admin from django.urls import path from rest_framework_jwt.views import obtain_jwt_token from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('login/',obtain_jwt_token), path('text/',views.TestView.as_view()), ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
认证(认证类):导入,配置在视图类上:
from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response class TestView(APIView): # 登录后才能访问:加一个认证类 # 使用jwt提供的认证类,必须配合权限类 authentication_classes = [JSONWebTokenAuthentication, ] permission_classes = [IsAuthenticated, ] def get(self, request): return Response('ok')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
前端访问时:token需要放在请求头中
Authorization:jwt token串
1.提出需求
我们要在登录成功以后,前端看到的格式不再单一的只有token,而是做成{code:100,msg:‘登录成功’,token:adfasdfasdf}
2.解决措施
固定写法:写一个函数,函数返回什么,前端就看到什么,配置在配置文件中
3.操作步骤
- 写一个函数(prompt.py文件内写)
from rest_framework_jwt.utils import jwt_response_payload_handler def jwt_response_payload_handler(token, >user=None, request=None): return { 'code': 100, 'msg': '登录成功', 'username': user.username, 'token': token }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 把函数配置在配置文件中
# djagnorestframework-jwt的配置 JWT_AUTH={ 'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.prompt.jwt_response_payload_handler', }
'
- 1
- 2
- 3
- 4
运行
- 以后登录接口返回的格式就是咱们写的函数的返回值
models.py中定义UserInfo表:
class UserInfo(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32)
- 1
- 2
- 3
写一个登录接口:
from rest_framework.exceptions import APIException from rest_framework_jwt.settings import api_settings jwt_payload_handler = >api_settings.JWT_PAYLOAD_HANDLER jwt_encode_handler = >api_settings.JWT_ENCODE_HANDLER from .models import UserInfo class UserView(APIView): def post(self, request): try: username = request.data.get('username') password = request.data.get('password') >user=UserInfo.objects.get(username=username,password=password) # 根据user,签发token---》三部分:头,荷载,签名 # 使用djagnorestframework-jwt模块提供的签发token的函数,生成token payload = jwt_payload_handler(user) # 通过user对象---》{username:lqz,id:1,过期时间} token=jwt_encode_handler(payload) # 根据payload---》得到token:头.荷载.签名 print(payload) print(token) return Response({'code':100,'msg':'登录成功','token':token}) except Exception as e: raise APIException('用户名或密码错误')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。