drf学习笔记
今日内容概要
- cookie,session,token介绍
- jwt原理介绍
- base64编码和解码
- drf-jwt快速使用
- drf-jwt修改返回格式
- 自定义user表,签发token
今日内容详细
cookie,session,token介绍
cookie是:存在客户端浏览器的键值对
session是:存在于服务端的键值对
两者区别:
session是存储服务器端,cookie是存储在客户端,所以session的安全性比cookie高
session的信息是通过sessionid获取的,而sessionid是存放在会话cookie中
token是:三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端:存在移动端中)
token:三段式
第一段:头:公司信息,加密方式...以字典形式{}
第二段:荷载:真正的数据{'name':'lqz', 'id':1}
第三段:签名:通过第一段和第二段,通过某种加密方式加密得到的
token的使用分两个阶段:
1.登陆成功后的【签发token阶段】,它生成了三段
2.登录成功访问某个接口的【验证阶段】,它验证token是否合法
注意:使用token的认证机制,服务端还要存数据吗?
token是服务端生成的,客户端保存,服务端不存储token。
详情链接:https://www.cnblogs.com/liuqingzheng/p/8990027.html
签发token阶段:
验证阶段:
jwt原理介绍
jwt:Json web token (JWT), token的应用于web方向的称之为jwt。
jwt的构成
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.
链接一起就构成了Jwt字符串。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一段:头(header)
jwt的头部承载两部分信息:
声明类型:这里是jwt
声明加密的算法:通常直接使用 HMAC SHA256
完整的头部就像下面这样的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后将头部进行base64编码,构成了第一部分:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
第二段:荷载(payload)
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
exp: jwt的过期时间,这个过期时间必须要大于签发时间
iat: jwt的签发时间
用户信息: 用户信息
一般可以用来存放用户信息:
{
"exp": "1234567890",
"name": "John Doe",
"userid": 3
}
然后将其进行base64编码,得到JWT的第二部分:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
第三段:签名(signature)
JWT的第三部分是一个签证信息,这个签证信息是把头和荷载通过某种加密方式+秘钥加密得到一个字符串:
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.连接成一个完整的字符串,构成了最终的jwt。
注意:secret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
jwt使用流程最核心的就是:
签发:登录接口签发
1)用账号密码访问登录接口,登录接口逻辑中调用签发token算法,得到token,返回给客户端,客户端自己存到cookies中
认证:认证类认证
2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户
注:登录接口需要做 认证 + 权限 两个局部禁用
基于session和jwt的认证原理:
签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
1)用基本信息存储json字典,采用base64算法加密得到 头字符串
2)用关键信息存储json字典,采用base64算法加密得到 体字符串
3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串
账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台
校验:根据客户端带token的请求 反解出 user 对象
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
base64编码和解码
base64:可以把字符串编码成base64的编码格式(等于大小写字母,数字和)
base64还可以把base64编码的字符串,解码回原来的格式
应用场景:
1. jwt中使用
2. 网络中传输字符串
3. 网络中传输图片(不常用)
import json
import base64
d = {'name': 'lqz', 'userid': 6, 'age': 19}
info = json.dumps(d)
print(info)
# 把字符串使用base64编码
res = base64.b64encode(info.encode('utf-8'))
print(res) # eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9
# 把字符串使用base64解码
d = 'eyJuYW1lIjogImxxeiIsICJ1c2VyaWQiOiA2LCAiYWdlIjogMTl9'
res = base64.b64decode(d)
print(res) # b'{"name": "lqz", "userid": 6, "age": 19}'
# 把图片保存起来看看
s = '图片的base64解码数据'
res=base64.b64decode(s)
with open('code.png','wb') as f:
f.write(res)
drf-jwt快速使用
可以帮我们快速实现 jwt: 签发(登录接口) 认证(认证类)
django中使用jwt:
1. 可以自己写
2. 使用模块djangorestframework-jwt:https://github.com/jpadilla/django-rest-framework-jwt (比较老)
3. 使用模块djangorestframework-simplejwt:https://github.com/jazzband/djangorestframework-simplejwt (比较新)
# 安装
pip3 install djangorestframework-jwt
# 快速使用
1. 迁移表,因为它默认使用auth的uesr表签发token
2. 创建超级用户(auth的user表中要有记录)
3. 签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
4. 认证(认证类):导入,配置在视图类上
class TestView(APIView):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated,]
5. 前端访问时,token需要放在请求头中
Authorization:jwt token串 (jwt空格token串)
drf-jwt修改返回格式
登录成功后,前端看到的格式,太固定了,只有token。
要求登录成功格式:
{
'code': 100,
'msg': '登录成功',
'token': 随机字符串
}
使用步骤:
1.写一个函数
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code':100,
'msg':'登录成功',
'username':user.username,
'token':token
}
2.把函数配置在配置文件中
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}
自定义user表,签发token
自己定义用户表,实现签发token功能,自己写登录接口。
# models.py 中定义UserInfo表
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
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('用户名或密码错误')
作业
# views.py
from django.shortcuts import render
# Create your views here.
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
from .models import User
from rest_framework.exceptions import APIException
from .auth import LoginAuth
class TestView(APIView):
#authentication_classes = [JSONWebTokenAuthentication, ]
#permission_classes = [IsAuthenticated, ]
authentication_classes = [LoginAuth, ]
def post(self, request):
return Response('ok')
from rest_framework_jwt.settings import api_settings
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 = User.objects.get(username=username, password=password)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(token)
return Response({'code': 100, 'msg': '登录成功', 'token': token})
except Exception as e:
raise APIException('用户名或密码错误')
# auth.py
from rest_framework_jwt.utils import jwt_response_payload_handler
from rest_framework.authentication import BaseAuthentication
import jwt
from rest_framework import exceptions
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': '登录成功',
'username': user.username,
'token': token,
}
from rest_framework_jwt.settings import api_settings
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
from .models import User
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_TOKEN')
try:
payload = jwt_decode_handler(token)
except jwt.ExpiredSignature:
msg = '签名已过期'
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = '解码签名时出错'
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
msg = '解析签名未知错误'
raise exceptions.AuthenticationFailed(msg)
user = User.objects.get(pk=payload['user_id'])
return user, token
# settings.py
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.auth.jwt_response_payload_handler',
}
# 请求头中:X_FORWARDED_FOR 代之什么?
X-Forwarded-For请求头格式非常简单,就这样:
X-Forwarded-For:client, proxy1, proxy2
可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,
最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,
IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
Proxy3 直连服务器,它会给 XFF 追加 IP2,表示它是在帮 Proxy2 转发请求。
列表中并没有 IP3,IP3 可以在服务端通过 remote_address 字段获得。
remote_address 来自 TCP 连接,表示与服务端建立 TCP 连接的设备 IP,在这个例子里就是 IP3。
对程序来说,读取的 request.remote_addr 也永远是直接跟他链接的 ip。
# 集群 ,分布式
集群:
集群是指在几个服务器上部署相同的应用程序来分担客户端的请求。
它是同一个系统部署在不同的服务器上。
集群主要的使用场景是为了分担请求的压力,但是,当压力增大的时候,可能需要存储的部分,比如mysql无法面对大量的写操作,因为MySQL做成集群后,主要的写压力还是在master(主机)的机器上,其他slave(从机)机器无法分担写压力,这时就引出了“分布式”!
分布式
分布式是指多个系统协同合作完成一个特定任务的系统。
它是不同的系统部署在不同的服务器上,然后服务器之间相互调用。
分布式解决了中心化管理的问题。把所有的任务叠加到一个节点处理,效率太慢,
所以把一个大的问题拆分成多个小问题,并分别解决,最终协同合作,。
分布式主要工作:分解任务,把任务拆解。
最后,再深入理解一下集群和分布式及其区别:
分布式:把一个大业务拆分成多个子业务,每个子业务都是一套独立的系统,子业务之间相互协作最终完成整体的大业务。
集群:把处理同一个业务的系统部署多个节点 。
把一套系统拆分成不同的子系统部署在不同服务器上,这叫分布式。
把多个相同的系统部署在不同的服务器上,这叫集群。部署在不同服务器上的相同系统必然要做“负载均衡”。
集群主要是简单加机器解决问题,对于问题本身不做任何分解。
分布式处理里必然涉及任务分解与答案归并。分布式中的某个子任务节点,可以是一个集群,该集群中的任一节点都作为一个完整的任务出现。
集群和分布式都是由多个节点组成,但集群中各节点间基本不需要通信协调,而分布式中各个节点的通信协调是必不可少的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了