文章目录
- 一、频率源码分析
- APIView的频率源码分析
- SimpleRateThrottle 频率类源码分析
- 二、项目自动生成API接口文档
- 1.CoreAPI
- 定义
- 文档接口描述信息
- 2.Swagger
- 3.接口文档规范
- 三、Cookie、Session、Token介绍
- 1.Cookie介绍
- 2.Session介绍
- 3.Cookie和Session的区别
- 4.Token介绍
- 四、JWT介绍
- 五、JWT的构成原理
- JWT的生成
- 1) header头部
- 2) payload荷载
- 3) signature签名
- 手动验证token
- 本质原理
- 六、Base64编码与解码
- 七、jwt的开发重点
一、频率源码分析
从最开始的DRF知识介绍到现在,我们也知道频率组件是在APIView类源码中的dispatch方法里面执行了三大认证,这三大认证也是在视图类方法之前就执行了。
APIView的频率源码分析
'这里我就不一个个罗列出来了,就直接写有关频率源码了。'def dispatch(self, request, *args, **kwargs):try:'执行三大认证'self.initial(request, *args, **kwargs)'APIView里面的initial方法' def initial(self, request, *args, **kwargs):'执行频率'self.check_throttles(request)'APIView里面的check_throttles方法'def check_throttles(self, request):throttle_durations = []'这里的self.get_throttles方法,就是获取视图类中一个个的频率类的对象,它是一个列表''然后循环遍历,把一个个的频率类的对象给了throttle'for throttle in self.get_throttles():'这里就是为什么我们继承BaseThrottle需要重写这个方法的原因。'if not throttle.allow_request(request, self):'''当这个方法的返回值为False时,则获取等待时间并添加到throttle_durations这个列表中这里的throttle.wait就是重写的wait方法的返回值,返回的还剩多长时间才能进行下一次的访问'''throttle_durations.append(throttle.wait()) '如果执行了频率限制,就会执行这里'if throttle_durations:'这里的列表表达式就是duration在这个throttle_durations列表中有还剩多长时间的数据'durations = [duration for duration in throttle_durationsif duration is not None # 这里它过滤掉了为空的]'然后在这里取出了这个列表中的最大值,例子:duration[33,54]'duration = max(durations, default=None) # 这里就会取出最大的那个54'这里的self就是视图类的对象,所以我们在APIView中找到了throttled方法'self.throttled(request, duration)'duration有两种情况,一种是有值(被限制频率),一种是None(没有被限制频率)''APIView中的throttled方法''我们可以看到在上面它把最后的duration传入到这个throttled方法中了'def throttled(self, request, wait):'所以这里的wait就是最后的druation的值,所以这里的wait也是有两种情况,有值或者None'raise exceptions.Throttled(wait)'''这里的exceptions.Throttled()就是from rest_framework.exceptions import Throttled也就是类实例化得到对象后把wait也就是duration里面的数字或者None传入进去,具体这个exceptions.Throttled方法里面是如果把我们限制的数字拼接到页面上的这里就不多介绍'''
SimpleRateThrottle 频率类源码分析
我们从上面
APIView
中的频率源码知道,继承SimpleRateThrottle频率类
和继承BaseThrottle频率类
,本质都是差不多,都是重写一些方法,而SimpleRateThrottle频率类
,它的源码中就是继承了BaseThrottle
频率类,所以这里我就直接看SimpleRateThrottle
的源码
'我们自己写自定义频率类的时候,继承SimpleRateThrottle频率类,发现并没有重写allow_request方法,而是写的别的''''所以我们在这里去SimpleRateThrottle源码中看看它是如何实现的,在SimpleRateThrottle源码中我们看到了它重写了allow_request方法,并且它就是继承了BaseThrottle类'''
class SimpleRateThrottle(BaseThrottle):def allow_request(self, request, view):"""参数:- request: 当前的请求对象- view: 请求对应的视图对象或处理函数返回:- True: 允许请求通过- False: 请求被限制注意: 这里的逻辑假设限流器的主要参数在初始化时已经设置,如速率(rate)、缓存键(key)、历史记录(history)等。"""'我们自己自定义的时候写了这个rate,例如:rate=5/m'if self.rate is None: # 当rate为空执行,直接没有限制return True'这里调用了get_chche_key(),这就是为什么我们自定义频率类重写的是这个方法''所以这里的self.key就是重写的get_chche_key方法的返回值,例如按照ip限制,那就是ip'self.key = self.get_cache_key(request, view)'这里self.key也就是get_chche_key方法的返回值是空的时候执行,相当于没有进行限制'if self.key is None:return True'''self.key就是get_chche_key方法的返回的限制self.cache.get就是缓存,默认设置缓存在内存中,然后去缓存中根据self.key的值,去取出访问者时间列表如果没有则是[]self.history就是当前ip,访问的时间列表'''self.history = self.cache.get(self.key, [])'这里的self.timer就是加括号调用time.time实例化对象self.now,拿到当前执行时间'self.now = self.timer() # timer = time.time没有加括号'''这里就是拿到访问列表的最后一个时间数据(也是最早的时间数据,这个我们继承BaseThrottle写自定义频率类的时候做过这个类似的逻辑)然后当它小于或者等于,当前时间减去self.duration也就是对应的60秒,当它超过了60秒就给超出的数据弹出'''while self.history and self.history[-1] <= self.now - self.duration:self.history.pop()'''这里跟我们继承BaseThrottle写自定义频率类时类似,就是判断这个列表中的时间数据有几个,当它大于我们我们设置的rate=5/m中的5个就执行'''if len(self.history) >= self.num_requests:'这里的self.throttle_failure方法也是SimpleRateThrottle源码内的方法,它直接返回了False'return self.throttle_failure()'这里我们就可以知道,可以在自定义频率类中重写这个self.throttle_failure方法,定制返回的文字''''这个selfthrottle_success方法跟上面一样,它直接返回True,def throttle_success(self):它把没有被限制的时间一个个放到history这个列表的第零个位置,这也就是我上面说的history[-1]是最早的时间self.history.insert(0, self.now)然后在这里又放入到缓存当中self.cache.set(self.key, self.history, self.duration)return True'''return self.throttle_success()'在上面我并没有去说self.duration和self.num_requests到底为什么是rate=5/m中的60和5。我在这里说一下''首先直接按住Ctrl加鼠标左键点击self.duration就跳转到了它的初始化方法了'def __init__(self):'这里可以看到self.duration和self.num_requests是self.parse_rate解压赋值的,所以我们去看看这个方法'self.num_requests, self.duration = self.parse_rate(self.rate)'这个parse_rate也是SimpleRateThrottle的方法'def parse_rate(self, rate):'当rate为空时,设置两个空'if rate is None: return (None, None)'当有值时按/进行切分,然后解压赋值给num、period两'num, period = rate.split('/') # 例子5/m 所以这里的num就是5,period就是m'然后再把/斜杠前的数据强转成数字类型后赋值给num-requests'num_requests = int(num) # 到了这一步我们就知道,为什么num_requests就是rate=5/m的5了'''这里可以看到duration就是根据period这个第一个字母(索引)的key去duration这个字典中取值,这里都是设置好的就是时分秒对应的秒数,这也是为什么我上面的duration说是60了因为对应的rate=5/m中的m就是minute分钟它这里的写法比较高级,直接取第一个字符,不管你后面写了什么只要对应上第一个字符就可以'''duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]'最后给这两个值返回出去'return (num_requests, duration)
二、项目自动生成API接口文档
在进行前后端分离开发模式,接口文档是很有必有的,前端通过接口文档可以得知访问什么接口可以得到什么样的数据,而后端只需要规定好接口返回的数据,并定义好接口文档就可以了。
REST framework可以自动帮助我们生成接口文档。
接口文档以网页的方式呈现。
自动接口文档能生成的是继承自APIView及其子类的视图。
1.CoreAPI
定义
CoreAPI是基于djangorestframework框架下的自动文档生成器,只要按DRF规则写的路由,CoreAPI就可以自动生成接口文档。
REST framewrok生成接口文档需要coreapi库的支持。作为依赖库使用
pip install coreapi
其实更推荐使用pycharm安装,因为可以指定装到哪个解释器里面。
在总路由中添加接口文档路径。
文档路由对应的视图配置为
rest_framework.documentation.include_docs_urls
参数
title
为接口文档网站的标题。
from rest_framework.documentation import include_docs_urlsurlpatterns = [path('docs/', include_docs_urls(title='站点页面标题'))]
效果如下:
文档接口描述信息
1、 单一方法的视图,可直接使用类视图的文档字符串
class BookListView(generics.ListAPIView):"""返回所有图书信息."""
2、包含多个方法的视图,在类视图的文档字符串中,分开方法定义
class BookListCreateView(generics.ListCreateAPIView):"""get:返回所有图书信息.post:新建图书."""
3、对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分
class BookView(ModelViewSet):"""list:返回图书列表数据create:新增图书接口retrieve:返回图书详情数据update:修改图书接口delete:删除图书接口"""
4.接口中的字段描述显示
class MovieSerializer(serializers.ModelSerializer):class Meta:model = models.Bookfields = '__all__'extra_kwargs = {'name': {'help_text': '书籍名称'},'price': {'help_text': '书籍价格'},}
效果如下:
2.Swagger
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许 API 来始终保持同步。Swagger 让部署管理和使用功能强大的 API 从未如此简单。
Swagger 的优势
- 支持 API 自动生成同步的在线文档:使用 Swagger 后可以直接通过代码生成文档,不再需要自己手动编写接口文档了,对程序员来说非常方便,可以节约写文档的时间去学习新技术。
- 提供 Web 页面在线测试 API:光有文档还不够,Swagger 生成的文档还支持在线测试。参数和格式都定好了,直接在界面上输入参数对应的值即可在线测试接口。
如何在DRF内使用Swagger?需要先安装指定模块
pip3 install drf_yasg
该模块还包含了可以生成
ReDoc
接口文档
DRF内的Swagger配置
from drf_yasg.views import get_schema_viewfrom drf_yasg import openapischema_view = get_schema_view(openapi.Info(title="API接口文档平台", # 必传(接口文档名称)default_version='v1', # 必传(定义版本)description="图书管理系统接口文档", # 描述信息terms_of_service="#",contact=openapi.Contact(email="1363004489@qqcom"), # (接口文档联系人)license=openapi.License(name="BSD License"), # 许可证),public=True,# permission_classes=(permissions.AllowAny,), # 指定权限类authentication_classes=[] # 防止该swagger接口收到认证拦截)
路由配置
urlpatterns = [path('admin/',admin.site.urls),re_path(r'^swagger(?P<format>\.json|\.yaml)$',schema_view.without_ui(cache_timeout=0), name='schema-json'),path('swagger/', schema_view.with_ui('swagger', cache_timeout=0),name='schema-swagger-ui'),path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schemaredoc'),]
那么此时我们就可以访问
swagger/
来接口swagger为我们提供的接口文档了,开头展示的为我们配置里面编写的内容:
接口描述如何编写,在方法的增加单一注释:
class GetAllBooks(ListAPIView):def list(self, request, *args, **kwargs):"""查询图书接口:param request::return: 返回所有图书数据"""
list
方法处理的就是get
请求
所有书籍接口查看
并且支持在线测试接口数据,通过Excute
来调用接口。
但我们如果使用了Authorization
,例如jwt
认证,那么我们则需要对swagger
进行一定的配置。
在settings.py
里面使用如下变量,给swagger
增加配置项:
SWAGGER_SETTINGS = {"SECURITY_DEFINITIONS": {"api_key": {"type": 'apiKey',"name": 'Authorization', # 存放Cookie的key"in": 'header' # 存放在请求头内}},}
此时页面会出现认证选项,那么我们只需要将Cookie拷贝进去就ok了
如果我们是根据SessionID
进行登录认证的话,那么可以增加如下配置,便可以在swagger
页面进行登录操作,无需手动赋值Cookie
进行认证。
SWAGGER_SETTINGS = {"SECURITY_DEFINITIONS": {"api_key": {"type": 'apiKey',"name": 'Authorization',"in": 'header'}},'LOGIN_URL': 'rest_framework:login', # DRF提供的登录行为'LOGOUT_URL': 'rest_framework:logout',}
接着我们还需要配置一下主路由
urlpatterns = [path('admin/', admin.site.urls),re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),# 配置如下路由放置最低端,这表示任意URL都可以生效,感兴趣的可以去看看rest_framework.urls源码,然后自己重写一套实现逻辑path('', include("rest_framework.urls", namespace="rest_framework")),]
现在点击swagger页面右边的Django Login按钮,出现如下界面便可进行登录
3.接口文档规范
-需要有描述-请求地址-请求方式-编码方式-请求参数----》参数的解释,是否必传,参数类型-请求体----》参数的解释,是否必传,参数类型-返回示例----》返回数据字段解释-错误码解释
三、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介绍转自:
https://www.cnblogs.com/liuqingzheng/p/17942227
四、JWT介绍
在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用Session认证机制,而使用Json Web Token(本质就是token)认证机制。
Json web token (JWT):是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
通俗的说
Json web token (JWT)
:是一种token
机制,主要用于前后端登录校验,区别与session
的认证机制,不需要在后端存储数据,重点是签发(登录后签发)
和认证(访问某些需要登录后才能访问的接口,需要携带认证信息)
token是一种不需要在服务端的一种认证机制。比如:当客户端第一次登陆时,服务端会响应一个token值给客户端,下次访问时携带这个token值,服务端会解析出这个token值里面的数据,以此来确保是一个合法的用户才能通过认证。
五、JWT的构成原理
JWT的生成
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.连接一起就构成了Jwt字符串。就像这样:
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload),第三部分是签证(signature)。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
1) header头部
JWT头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。
比如:
- 声明类型:这里是jwt
- 声明加密的算法,通常直接使用 HMAC、HS256、MD5都可以
这也可以被表示成一个JSON对象
import base64import jsonhead = {'type': 'JWT','alg': H5256,}head_json = json.dumps(head).encode('utf-8') # base64只能加密bytes类型内容head_base64 = base64.b64encode(head_json) # 对头部head数据进行base64编码加密
然后将头部进行base64编码(该编码是可以对称解码的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2) payload荷载
荷载就是存放有效信息的地方。例如用户信息、签发时间、过期时间等,这些有效信息包含三个部分
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
iss:
jwt签发者sub:
jwt所面向的用户aud:
接收jwt的一方exp:
jwt的过期时间,这个过期时间必须要大于签发时间nbf:
定义在什么时间之前,该jwt都是不可用的.iat:
jwt的签发时间jti:
jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。
公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分存入客户端后,不法用户可自行解码.
私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解码的,意味着该部分信息可以归类为明文信息。
定义一个payload
import base64import jsonpayload = {"sub": "1234567890","name": "Tom","admin": True}payload_json = json.dumps(payload).encode('utf-8')payload_base64 = base64.b64encode(payload_json)
然后将其进行base64加密,得到JWT的第二部分
eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIlRvbSIsICJhZG1pbiI6IHRydWV9
3) signature签名
JWT的第三部分是一个签证信息,通过加密方式把第一段和第二段通过秘钥加密得到的,这个签证信息由三部分组成:
- header (base64编码加密后的)
- payload (base64编码加密后的)
- secret(秘钥、也可以称为加盐)
import hashlibmd5 = hashlib.md5()md5.update(head_base64)md5.update(payload_base64)md5.update(b'wmwm') # secretsignature = md5.hexdigest()token = f'{head_base64.decode("utf-8")}.{payload_base64.decode("utf-8")}.{signature}'print(token)
第三段signature签名值:
04748f59f146cdcd27e1452c4cd819e5
最终返回给我们的就是一个完整的token值
eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJNRDUifQ==.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIlRvbSIsICJhZG1pbiI6IHRydWV9.04748f59f146cdcd27e1452c4cd819e5
我们只需要将此token返回给前端,下次访问服务端时携带token,服务端会检查前前两段经过md5算法,以及指定秘钥后是否和签名值相同。
手动验证token
比如:当客户端将token携带过来以后,我们需要对其token值是否有效进行校验
import hashlib# 获取到jwt的3段值head,payload,signature = 'eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJNRDUifQ==.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogIlRvbSIsICJhZG1pbiI6IHRydWV9.04748f59f146cdcd27e1452c4cd819e5'.split('.')# 将前两段进行md5加密,得到签证md5 = hashlib.md5()md5.update(head.encode('utf-8'))md5.update(payload.encode('utf-8'))md5.update(b'wmwm') # 使用和生成签证使用的加盐秘钥相同(尤为重要)# 校验前两段值经过md5加密以及加盐后,是否和jwt第三段值相同,如果是的话,则说明客户端携带的token值合法(与我们当时响应的相同)if md5.hexdigest() == signature:print('认证成功')
注意:
secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
本质原理
jwt认证算法:签发与校验
"""1)jwt分三段式:头.体.签名 (head.payload.sgin)2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息{"company": "公司信息",...}5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间{"user_id": 1,...}6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密{"head": "头的加密字符串","payload": "体的加密字符串","secret_key": "安全码"}"""
签发:根据登录请求提交来的 账号 + 密码 + 设备信息 签发 token
"""1)用基本信息存储json字典,采用base64算法加密得到 头字符串2)用关键信息存储json字典,采用base64算法加密得到 体字符串3)用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到 签名字符串账号密码就能根据User表得到user对象,形成的三段字符串用 . 拼接成token返回给前台"""
校验:根据客户端带token的请求 反解出 user 对象
"""1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的3)再用 第一段 + 第二段 + 服务器安全码(在内部定义的加盐秘钥) 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户"""
drf项目的jwt认证开发流程(重点)
"""1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies、或其它位置2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户注:登录接口需要做 认证 + 权限 两个局部禁用"""
六、Base64编码与解码
首先Base64是python内置的模块 base64可以把字符串编码成base64的编码格式(大小写字母 包括数据与=号)
eyJOYW1lIjogIkxpa2UiLCAiVXNlcklEIjogODgsICJhZ2UiOiAyMH0=
base64可以把base64编码的网络传输的字符串、图片解码回原来的格式
使用Base64编码
import jsonimport base64d = {'name':'jack','user_id':88,'age':20}user = json.dumps(d)print(user) # {"name": "jack", "user_id": 88, "age": 20}res = base64.b64encode(user.encode('utf-8')) # 编码print(res) # eyJuYW1lIjogImphY2siLCAidXNlcl9pZCI6IDg4LCAiYWdlIjogMjB9res1 = base64.b64decode(res) # 解码print(res1) # {"name": "jack", "user_id": 88, "age": 20}
使用Base64解码
img = 'iVBORw0KGgoAAAANSUhEUgAAAMcAAADH' # 这里省略code = base64.b64decode(img)with open('code.png','wb')as f:f.write(code)
七、jwt的开发重点
从上面几个jwt的知识点下来,我们知道
jwt
就是一种token
机制,主要就是用于前后端登录认证的方式
1.登录:签发token-----》登录接口1).用户携带用户名、密码到后端2).校验用户名密码是否正确3).如果正确,签发token,按照jwt逻辑生成三段式,返回给前端看2.认证token------>认证类1.用户访问我们需要登录的接口2.携带token过来------>请求头、请求地址等3.后端校验用户携带的token。是否被篡改、伪造4.如果没问题,认证通过,继续后续的逻辑