文章目录
一、Cookie Session Token介绍
1)Cookie介绍
cookie 是一个非常具体的东西 指的就是浏览器里面能永久存储的一种数据 仅仅是浏览器实现的一种数据存储功能
- cookie由服务器生成,发送给浏览器,浏览器把cookie以kv形式保存到某个目录下的文本文件内,下一次请求同一网站时会把该cookie发送给服务器。由于cookie是存在客户端上的,所以浏览器加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间,所以每个域的cookie数量是有限的。
2)Session介绍
session 从字面上讲,就是会话。这个就类似于你和一个人交谈,你怎么知道当前和你交谈的是张三而不是李四呢?对方肯定有某种特征(长相等)表明他就是张三。
session 也是类似的道理,服务器要知道当前发请求给自己的是谁。为了做这种区分,服务器就要给每个客户端分配不同的“身份标识”,然后客户端每次向服务器发请求的时候,都带上这个“身份标识”,服务器就知道这个请求来自于谁了。至于客户端怎么保存这个“身份标识”,可以有很多种方式,对于浏览器客户端,大家都默认采用 cookie 的方式。
服务器使用session把用户的信息临时保存在了服务器上,用户离开网站后session会被销毁。这种用户信息存储方式相对cookie来说更安全,可是session有一个缺陷:如果web服务器做了负载均衡,那么下一个操作请求到了另一台服务器的时候session会丢失。
3)Cookie和Session的区别
session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高
- 获取session里的信息是通过存放在会话cookie里的session id获取的。而session是存放在服务器的内存中里,所以session里的数据不断增加会造成服务器的负担,所以会把很重要的信息存储在session中,而把一些次要东西存储在客户端的cookie里。
session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中
- 当浏览器关闭的时候会话cookie消失,所以sessionid也就消失了,但是session的信息还存在服务器端,只是查不到所谓的session,但它并不是不存在
4)Token介绍
无状态、可扩展
- 在客户端存储的 token 是无状态的,并且能够被扩展。基于这种无状态和不存储Session信息,负载均衡服务器 能够将用户的请求传递到任何一台服务器上,因为服务器与用户信息没有关联。相反在传统方式中,我们必须将请求发送到一台存储了该用户 session 的服务器上(称为Session亲和性),因此当用户量大时,可能会造成 一些拥堵。使用 token 完美解决了此问题。
安全性
请求中发送 token 而不是 cookie,这能够防止 CSRF(跨站请求伪造) 攻击。即使在客户端使用 cookie 存储 token,cookie 也仅仅是一个存储机制而不是用于认证。另外,由于没有 session,让我们少我们不必再进行基于 session 的操作。
Token 是有时效的,一段时间之后用户需要重新验证。我们也不一定需要等到token自动失效,token有撤回的操作,通过 token revocataion可以使一个特定的 token 或是一组有相同认证的 token 无效。
可扩展性
使用 Tokens 能够与其它应用共享权限。例如,能将一个博客帐号和自己的QQ号关联起来。当通过一个 第三方平台登录QQ时,我们可以将一个博客发到QQ平台中。
使用 token,可以给第三方应用程序提供自定义的权限限制。当用户想让一个第三方应用程序访问它们的数据时,我们可以通过建立自己的API,给出具有特殊权限的tokens。
多平台与跨域
我们已经讨论了CORS (跨域资源共享)。当我们的应用和服务不断扩大的时候,我们可能需要通过多种不同平台或其他应用来接入我们的服务。
可以让我们的API只提供数据,我们也可以从CDN提供服务(Having our API just serve data, we can also make the design choice to serve assets from a CDN.)。 在为我们的应用程序做了如下简单的配置之后,就可以消除 CORS 带来的问题。只要用户有一个通过了验证的token,数据和资源就能够在任何域上被请求到。
Access-Control-Allow-Origin: *
基于标准
- 有几种不同方式来创建 token。最常用的标准就是 JSON Web Tokens。很多语言都支持它
以上Cookie与Session和Token介绍借鉴于:(Cookie与Session介绍博主之前文章介绍过)
https://www.cnblogs.com/liuqingzheng/p/8990027.html
二、JWT介绍
在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT的构成
jwt就是一段字符串 由三段信息构成 将这三段信息用.链接起来构成jwt字符串
第一部分我们称为头部Header 第二部分称为荷载Payload 第三部分是签证Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
header
- jwt的头部承载两部分信息(类型是jwt 机密的算法通常使用HMAC SHA256)
{
'type':'jwt'
'alg': HS256,
}
- 然后讲头部进行加密(该加密是可以对称解密的) 构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Payload
载荷就是存放有效信息的地方 这个名字像是特指飞机上承载的货品 这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
定义一个Payload
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后将其进行base64加密,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
Signature
JWT的第三部分是一个签证信息,这个签证信息由三部分组成:
- header (base64后的)
- payload (base64后的)
- secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了 关于签发和核验JWT,我们可以使用Django REST framework JWT扩展来完成。
三、Base64编码与解码
首先Base64是python内置的模块 base64可以把字符串编码成base64的编码格式(大小写字母 包括数据与=号)
eyJOYW1lIjogIkxpa2UiLCAiVXNlcklEIjogODgsICJhZ2UiOiAyMH0=
base64可以把base64编码的网络传输的字符串、图片解码回原来的格式
使用Base64编码
import json
import base64
L = {'Name': 'Like', 'UserID': 88, 'age': 20}
userinfo = json.dumps(L)
print(userinfo) # {"Name": "Like", "UserID": 88, "age": 20}
res = base64.b64encode(userinfo.encode('utf-8'))
print(res) # eyJOYW1lIjogIkxpa2UiLCAiVXNlcklEIjogODgsICJhZ2UiOiAyMH0=
res1 = base64.b64decode(res)
print(res1) # {"Name": "Like", "UserID": 88, "age": 20}
使用Base64解码
img = 'iVBORw0KGgoAAAANSUhEUgAAAMcAAADHC==' # 经过base64编码的图片
code = base64.b64decode(img) # 进行解码
with open('code.png', 'wb')as f: # wb模式新建写入
f.write(code)
四、JWT快速使用
JWT主要用于签发登录接口需要配合认证类 JWT目前有两种 Jtw&Simplejwt(jwt比较老了 simple现在比较流行)
安装jwt
pip install djangorestframework-jwt
快速使用jwt签发
- 1.创建一个新项目 直接迁移表 因为它默认使用auth的User表签发Token
- 2.创建超级用户createsuperuser(右侧的auth_user表中需要有记录)
- 3.不需要写任何接口 如果使用的auth_user表作为用户表 它可以快速签发
- 4.签发登录: 只需要在路由中配置(因为它帮我们写好了登录接口)
导入模块
from rest_framework_jwt.views import obtain_jwt_token
配置路由
urlpatterns = [
path('login/', obtain_jwt_token),
]
这个时候直接访问即可 它都帮我们写好了 请求体中带入超级用户的账号密码就会返回Token了
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imxpa2UiLCJleHAiO......."
但是目前只有签发的功能 即然都我们写了登录 肯定也有权限功能 随便写一个视图类 给它加上认证
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class JwtView(APIView):
authentication_classes = [JSONWebTokenAuthentication, ] # 登录认证
permission_classes = [IsAuthenticated, ] # 权限认证
def get(self, request):
return Response('JWT认证啦!!!')
'''
这个时候直接访问我们的接口 会发生报错 "detail": "身份认证信息未提供。"
因为我们访问的时候需要带上Jwt的Token
固定格式:Authorization: jwt < 注意这里有一个空格 后面填写签发过后的Token
Authorization:[jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLC]
'''
五、JWT处理返回格式
目前我们写完了基于auth_user表完成签发登录 但是它返回的格式太固定了只有Token 但是我们想自定义格式呢?
ex: {code:100, msg:'登录成功', user:'like', token:JustYourSelf}
去response返回值中查看源码发现是返回数据的是jwt_response_payload_handler函数 所以我们重写它就好了
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
}
这个时候朝我们登录接口发送登录请求就会返回我们自定义的信息了
{
"code": 100,
"msg": "登录成功",
"username": "like",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imxpa2UiLCJleHA"
}
六、自定义User表 签发Token
上面我们都是基于auth_user表去做签发认证的 但是我们日常基本程序都是用自己定义的表来做认证的 所以我们该怎么做呢?
创建User表模型
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
写一个登录接口
from rest_framework.exceptions import APIException
from rest_framework_jwt.settings import api_settings
from .models import UserInfo
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
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)
payload = jwt_payload_handler(user) # 根据User 签发token > 头部,荷载,签名
token = jwt_encode_handler(payload) # 使用DjangoRestWork-jwt模块提供的签发token的函数,生成token
return Response({'code': 100, 'msg': '登录成功', 'token': token})
except Exception as e:
raise APIException('用户名或密码错误')
路由配置
urlpatterns = [
path('authlogin/', views.UserView.as_view()),
]
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点点赞收藏+关注
谢谢支持 !!!