drf从入门到精通---day09(接口文档,cookie session token 发展史,jwt介绍和原理,jwt快速使用,定制返回格式,jwt的认证类的使用,jwt快速使用总结)
上节回顾
# 1 认证类的执行流程---》源码分析
-请求进来---》路由匹配成功----》执行path('test/', view.BookView.as_view()),
--->继承了APIView----》APIView的as_view()内部的闭包函数view----》这个view中执行了
self.dispatch--->APIView的dispatch----》
def dispatch(self, request, *args, **kwargs):
# 包装了新的request
request = self.initialize_request(request, *args, **kwargs)
。。。
# 执行3大认证
self.initial(request, *args, **kwargs)
# 下面执行视图类的方法
def initial(self, request, *args, **kwargs):
# 认证
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
def perform_authentication(self, request):
request.user
####### Request 新的Request内部找 user
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors(): # 上下文管理器----》面试
self._authenticate()
return self._user
### 核心代码
def _authenticate(self):
# self.authenticators---》列表[认证类对象1,认证类对象2]
# authenticator 是认证类的对象
for authenticator in self.authenticators:
try:
#认证类对象.authenticate self 是新的request对象
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
#self 是新的request
# request.user 就是当前登录用户
# request.auth 一般把token给它
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
# self.authenticators:是什么时候传入的?执行__init__ 就是在Request初始化的时候传入的
-在APIView的dispatch的self.initialize_request(request, *args, **kwargs)初始化的,
return Request(
request,
parsers=self.get_parsers(),
#在这里传入的
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
-APIView的--get_authenticators()----》return [auth() for auth in self.authentication_classes]
-self.authentication_classes:视图类中配置的一个个的认证类的列表,如果没配,配置文件中,内置配置文件中
# 2 权限类的执行流程
# 3 频率类的执行流程
# 4 自己定义了一个频率类,基于BaseThrottle,重写allow_request
# 5 SimpleRateThrottle
-继承它,写代码少
-只需要重写get_cache_key和scope类属性,配置文件配置
-源码:allow_request----》就是咱们上面写的,可扩展性高,好多东西从配置文件取的
# 6 全局异常处理
-源码中,在3大认证,视图类的方法中如果出错,就会执行:self.handle_exception(exc)
def handle_exception(self, exc):
# 去配置文件中找到:EXCEPTION_HANDLER对应的函数,exception_handler
exception_handler = self.get_exception_handler()
# exception_handler(exc,context)
response = exception_handler(exc, context)
return response
-自己再配置文件中配置,以后出了异常,走咱们自己的,有两个参数exc, context
-exc错误对象
-context:上下文,包含 view,request。。。。
.
.
.
.
今日内容
1 接口文档
# 前后端分离
-我们做后端,写接口
-前端做前端,根据接口写app,pc,小程序
-作为后端来讲,我们很清楚,比如登录接口 /api/v1/login/---->post----
>username,password 编码方式json----》返回的格式 {code:100,msg:登录成功}
-后端人员,接口写完,一定要写接口文档
--------------------------------------------------
# 接口文档如何编写
-1 使用word,md 编写接口文档
-2 使用第三方平台,编写我们的接口文档(非常多)---》蛮好用的
-https://www.showdoc.com.cn/item/index
-3 公司自己使用第三方开源的搭建的---》Yapi ---》你如果想自己搭建
-https://zhuanlan.zhihu.com/p/366025001
-4 使用drf编写的接口,可以自动生成接口文档
swagger---》drf-yasg---》官方推荐使用
coreapi----》咱们讲
--------------------------------------------------
# 使用coreapi自动生成接口文档步骤
- 1 安装
pip3.8 install coreapi -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
- 2 配置路由
from rest_framework.documentation import include_docs_urls
path('docs/', include_docs_urls(title='xx项目接口文档')),
-3 在视图类,方法上,写注释即可
-在类上加注释
-在类的方法上加注释
-在序列化类或表模型的字段上加 help_text,required。。。。
-4 配置文件配置
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',}
-5 访问地址:http://127.0.0.1:8000/docs
--------------------------------------------------
# 接口文档,需要有的东西
-描述
-地址
-请求方式
-请求编码格式
-请求数据详解(必填,类型)
-返回格式案例
-返回数据字段解释
-错误码
.
模型表里面字段设置为null=True,接口文档里面就会不带required标签了
.
加描述里面加中文,就加键值对就行了 'help_text':'作者细节'
.
.
.
.
2 cookie session token 发展史
Cookie
HTTP无状态协议,是指协议对于事务处理没有记忆能力,之前做了啥完全记不住,
每次请求都是完全独立互不影响的,没有任何上下文信息。
缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量
增大,来实现上下文和状态的交互。
假如一直用这种原生无状态的http协议,我们每换一个页面可能就得重新登录一次,那还玩个球。
解决办法:
客户端每次请求时把自己必要的信息封装发送给服务端,服务端验证一下就行。
客户端第一次请求之后,服务端就开始做记录,然后客户端在后续请求中只需要将最基本
最少的信息发过来就行,不需要太多信息了。
Cookie方案
Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie。
内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短暂的。
硬盘Cookie保存在硬盘里,有一个过期时间,除非用户手工清理或到了过期时间,硬盘Cookie不会
被删除,其存在时间是长期的。
所以,按存在时间,可分为非持久Cookie和持久Cookie。
Cookie定义和作用
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在
本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
通常Cookie用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie
使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
Cookie 主要用于以下三个方面:
会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
个性化设置(如用户自定义设置、主题等)
浏览器行为跟踪(如跟踪分析用户行为等)
服务端创建Cookie
当服务器收到 HTTP 请求时,服务器可以在响应头里面添加一个 Set-Cookie 选项。
浏览器收到响应后通常会保存下 Cookie,之后对该服务器每一次请求中都通过 Cookie
请求头部将 Cookie 信息发送给服务器。另外,Cookie 的过期时间、域、路径、有效期、
适用站点都可以根据需要来指定。
B/S的Cookie交互
服务器使用 Set-Cookie 响应头部向用户浏览器发送 Cookie信息。
一个简单的 Cookie 可能像这样:
Set-Cookie: <cookie名>=<cookie值>
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
客户端对该服务器发起的每一次新请求,浏览器都会将之前保存的Cookie信息通过 Cookie
请求头部再发送给服务器。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
cookie存在的问题
Cookie 常用来标记用户或授权会话,被浏览器发出之后可能被劫持,被用于非法行为,可能
导致授权用户的会话受到攻击,因此存在安全问题。
还有一种情况就是跨站请求伪造CSRF,简单来说 比如你在登录银行网站的同时,登录了一个
钓鱼网站,在钓鱼网站进行某些操作时可能会获取银行网站相关的Cookie信息,向银行网站
发起转账等非法行为。
不过这种情况有很多解决方法,特别对于银行这类金融性质的站点,用户的任何敏感操作都
需要确认,并且敏感信息的 Cookie 只能拥有较短的生命周期。
同时Cookie有容量和数量的限制,每次都要发送很多信息带来额外的流量消耗、复杂的行为
Cookie无法满足要求。
特别注意:以上存在的问题只是Cookie被用于实现交互状态时存在的问题,但并不是说Cookie
本身的问题。
Session
Cookie机制在最初和服务端完成交互后,保持状态所需的信息都将存储在客户端,后续直接
读取发送给服务端进行交互。
Session代表服务器与浏览器的一次会话过程,并且完全有服务端掌控,实现分配ID、会话信息
存储、会话检索等功能。
Session机制将用户的所有活动信息、上下文信息、登录信息等都存储在服务端,只是生成一个
唯一标识ID发送给客户端,后续的交互将没有重复的用户信息传输,取而代之的是唯一标识ID,
暂且称之为Session-ID吧。
简单的交互流程
当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法
算出一个session的ID,用来标识该session对象。
当浏览器下次请求别的资源的时候,浏览器会将sessionID放置到请求头中,服务器接收到请求后
解析得到sessionID,服务器找到该id的session来确定请求方的身份和一些上下文信息。
Session的实现方式
首先明确一点,Session和Cookie没有直接的关系,可以认为Cookie只是实现Session机制的
一种方法途径而已,没有Cookie还可以用别的方法。
Session和Cookie的关系就像加班和加班费的关系,看似关系很密切,实际上没啥关系。
session的实现主要两种方式:cookie与url重写,而cookie是首选方式,因为各种现代浏览器
都默认开通cookie功能,但是每种浏览器也都有允许cookie失效的设置,因此对于Session机制
来说还需要一个备胎。
将会话标识号以参数形式附加在超链接的URL地址后面的技术称为URL重写。
原始的URL:
http://taobao.com/getitem?name=baymax&action=buy
重写后的URL:
http://taobao.com/getitem?sessionid=1wui87htentg&?name=baymax&action=buy
存在的问题
由于Session信息是存储在服务端的,因此如果用户量很大的场景,Session信息占用的空间
就不容忽视。
对于大型网站必然是集群化&分布式的服务器配置,如果Session信息是存储在本地的,那么由于
负载均衡的作用,原来请求机器A并且存储了Session信息,下一次请求可能到了机器B,此时
机器B上并没有Session信息。
这种情况下要么在B机器重复创建造成浪费,要么引入高可用的Session集群方案,引入Session
代理实现信息共享,要么实现定制化哈希到集群A,这样做其实就有些复杂了。
Token方案
Token是令牌的意思,由服务端生成并发放给客户端,具有时效性的一种验证身份的手段。
Token避免了Session机制带来的海量信息存储问题,也避免了Cookie机制的一些安全性问题,
属于典型的时间换空间的思路。在现代移动互联网场景、跨域访问等场景有广泛的用途。
4.1 简单的交互流程
客户端将用户的账号和密码提交给服务器
服务器对其进行校验,通过则生成一个token值,将其保存在数据库,同时也返回给客户端,
作为后续的请求交互身份令牌(注:这是简单的实现,实际大多数情况下,
服务端是不保存token的!!!)
客户端拿到服务端返回的token值后,可将其保存在本地,以后每次请求服务器时都携带该token,
提交给服务器进行身份校验
服务器接收到请求后,解析出其中的Token,再根据相同的加密算法和参数生成Token与客户端的Token进行对比,一致则通过,否则拒绝服务
Token验证通过,服务端就可以根据该Token中的uid获取对应的用户信息,进行业务请求的响应
Token的设计思想
以JSON Web Token(JWT)为例,Token主要由3部分组成:
Header头部信息
记录了使用的加密算法信息
Payload 净荷信息
记录了用户信息和过期时间等
Signature 签名信息
根据header中的加密算法和payload中的用户信息以及密钥key来生成,是服务端验证服务端
的重要依据
header和payload的信息不做加密,只做一般的base64编码,服务端收到token后剥离出header
和payload获取算法、用户、过期时间等信息,然后根据自己的加密密钥来生成signature,
并与客户端的sign进行一致性验证。
这样就实现了用CPU加解密的时间换取存储空间,干净利落,同时服务端密钥的重要性就显而易见,
一旦泄露整个机制就崩塌了,这个时候就需要考虑HTTPS了。
Token方案的特点
Token可以跨站共享,实现单点登录
Token机制无需太多存储空间,Token包含了用户的信息,只需在客户端存储状态信息即可,
对于服务端的扩展性很好
Token机制的安全性依赖于服务端加密算法和密钥的安全性
Token机制也不是万金油
总结
Cookie、Session、Token这三者是不同发展阶段的产物,并且各有优缺点,三者也没有明显的
对立关系,反而常常结伴出现,这也是容易被混淆的原因。
Cookie侧重于信息的存储,主要是客户端行为,Session和Token侧重于身份验证,
主要是服务端行为。
注以上 Cookie、Session、Token 内容转载于 https://zhuanlan.zhihu.com/p/467541615
token总结:服务端将头部信息与荷载信息用加密算法与密钥处理生成签名,
最后将头部信息与荷载与签名一起用base64编码后,就可以传给浏览器了,
当浏览器发送请求时,请求头里面带着token,后端拿到token后,先用base64解码后,
拿着解码后荷载,用用加密算法与密钥再处理生成签名,
然后比对生成的签名与解码的签名是否一致!!!
.
.
.
.
3 jwt介绍和原理
Json web token (JWT) 就是web方向token的使用
#JWT的构成 三部分,每部分用 . 分割
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
- 头:header
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
公司信息。。。
-荷载:payload
-存放有效信息的地方
-过期时间
-签发时间
-用户id
-用户名字。。。
-签名:signature
-第一部分和第二部分通过秘钥+加密方式得到的
----------------------------------------------
# jwt开发重点
-登录接口----》签发token
-认证类-----》jwt认证
----------------------------------------------
# base64编码和解码
import base64
import json
dic={'user_id':1,'username':"lqz"}
dic_str=json.dumps(dic)
#把这个字符串使用base64编码
res=base64.b64encode(dic_str.encode('utf-8'))
print(res)
# 解码
res=base64.b64decode('TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ=')
print(res)
# 注意:base64编码后,字符长度一定是4的倍数,如果不是,使用 = 补齐, = 不表示数据
----------------------------------------------
# base64 应用场景
'''
1 jwt 使用了base64
2 网络中传输数据,也会经常使用 base64编码
3 网络传输中,有的图片使用base64编码
'''
s='图片文件二进制数据经过base64编码后的数据'
res=base64.b64decode(s)
with open('a.png','wb') as f:
f.write(res)
# wb模式写入文件,图片出解出来了
.
.
.
.
.
3 drf-jwt快速使用
#django+drf 平台开发jwt这套,有两个模块
-djangorestframework-jwt ---》一直可以用
-djangorestframework-simplejwt---》公司用的多---》希望你们试一下
-也可以自己封装jwt签发和认证
-------------------------------------------
# 使用步骤
- 1 安装
pip3.8 install djangorestframework-simplejwt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
---------------------------------------
- 2 快速签发token(就是登录功能)---》登录接口都不用写了!!!
基于django自带的auth表里面的字段对应的信息就可以完成快速签发,也就是如果用的就是auth_user表或者继承了auth_user又增加了字段,都可以使用快速签发!!!
路由中这样一配置
from rest_framework_jwt.views import obtain_jwt_token
path('login/', obtain_jwt_token),
现在只要向'login/'路由发送请求,携带着用户名与密码,就能签发token成功,连登录的函数都不用写!!!
------------------------------------------
-3 postman,向http://127.0.0.1:8000/login/发送post请求,携带username和password数据
.
.
可以用createsuperuser命令快速创建管理用户,设置账户密码,
向login/ 路由发送post请求,携带username和password数据, 就可以快速生成token,完成了登录校验
.
.
.
.
.
4 定制返回格式
# 以后,如果是基于auth的User表签发token,就可以不自己写了,但是登录接口返回的格式,只有token,不符合公司规范
# 使用步骤
1 写个函数:jwt_response_payload_handler
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': '登录成功',
'token': token,
'username': user.username
# 'icon':user.icon
}
2 配置一下 ,项目配置文件
JWT_AUTH = {'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',}
3 使用postman测试,就能看到返回的格式了
.
.
.
.
5 jwt的认证类的使用!!!
# 需求:接口要登录后才能访问的使用,怎么办?
1 在视图类上加一个jwt自带的认证类,必须再配合一个drf提供的权限类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class BookView(ViewSetMixin, RetrieveAPIView):
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated] # 登录用户有权限,不登录用户没权限
pass
# jwt自带的JSONWebTokenAuthentication认证类,当你带token的时候,才会校验,不带就不校验,就直接过了,所以才要配合drf的权限了类,来确认用户是否登录的!!
2 postman测试
-请求头中key值叫Authorization
-请求头的value值是: jwt 有效的token值
# 当后端给需要登录才能访问的函数加了jwt自带的认证类后,
# 那么当我们需要访问登录后才能访问的接口时,在请求头里面携带token,就必须要
# key值叫Authorization value值是jwt 有效的token值
# 才能过认证,因为源码里面就是按Authorization作为键,取值,并按空格切割索引1,拿token的
.
.
.
.
6 drf-jwt快速使用总结!!!
如果项目就是用的auth_user表,或者继承auth_user表又增加了新的字段,
那么就可以使用token快速签发功能,也就是登录功能
1. 路由配置一下
from rest_framework_jwt.views import obtain_jwt_token
path('login/', obtain_jwt_token),
2. 如果需要定制返回格式,项目配置文件配置一下,并建格个utils.py文件,里面写代码如下:
JWT_AUTH = {'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',}
---------
def jwt_response_payload_handler(token, user=None, request=None):
return {
'status': 100,
'msg': '登录成功',
'token': token,
'username': user.username
}
3. 已经结束了,只要你朝'login/'路由,发的是post请求,请求体里面携带用户名与密码数据,
就会执行obtain_jwt_token()函数就会自动完成登录认证并签发token
---------------------------------
.
.
.
jwt 与 simplejet用法区别(不详细,simplejet不大会)
jwt的用法
路由
from rest_framework_jwt.views import obtain_jwt_token
path('login/', obtain_jwt_token),
视图类
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
在需要的类里面配置下面两行代码就行了
authentication_classes = [JSONWebTokenAuthentication]
permission_classes = [IsAuthenticated] # 登录用户有权限,不登录用户没有权限,所以没带token被它拦住了!
定制返回格式,在settings里面配一下
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}
再建个utils文件,里面定义函数
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': '登录成功',
'token': token,
'username': user.username
}
postman里面发请求的时候
请求头里面 Authorization 对应的值要以jwt开头加空格再带token才能正常过登录权限认证
.
.
simplejet用法
路由
from rest_framework_simplejwt.views import token_obtain_sliding
path('login/', token_obtain_sliding),
视图类
from rest_framework_simplejwt.authentication import JWTAuthentication
在需要的类里面配置
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
# 登录用户有权限,不登录用户没有权限,所以没带token被它拦住了!
settings里面
SIMPLE_JWT = {
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.SlidingToken",),
}
postman里面发请求的时候
请求头里面 Authorization 对应的值要以Bearer开头加空格再带token才能正常过登录权限认证
.
.
作业
1 自动生成接口文档试一下
2 使用drf-jwt快速签发和认证token,定制返回格式
-----------
3 使用simplejwt,签发token
4 drf-jwt 登录接口怎么写的
-它把逻辑写在了序列化类中----》全局钩子中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY