今日内容概要
- RBAC
- 自动生成接口文档
- jwt介绍与快速使用
- jwt定制返回格式
- jwt源码分析
内容详细
1、RBAC(重要)
# RBAC 是基于角色的访问控制(Role-Based Access Control )
在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
# 对外的权限比较简单:
普通注册用户,VIP用户,超级VIP --》优酷,网易云音乐,百度网盘
# 公司内部系统:通常使用RBAC的权限控制
公司内有部门(开发部,运维部,市场部,总裁办,人力资源部门)
权限和角色(部门)绑定
举个例子:发工资权限,招人权限,开发代码权限...
招人权限,发工资权限给人力资源
开发代码权限给开发部门
# 权限赋予给角色(部门),又把角色(部门)又赋予用户
"""
python用来做公司内部项目居多:
人事系统,进销存,报销审批,自动化运维
原因:
公司内部项目对执行效率要求不高(人少)
对开发效率要求高(越快开发出越好,成本越低越好)
知乎,豆瓣用python写的---》随着用户量增大---》切换语言
"""
# 针对于公司内部项目,后台管理居多(运营在使用),使用rbac居多
django-vue-admin:后端用drf,前端用vue,权限管理的 脚手架---》前后端分离
django的admin---》混合的后台管理用的多---》基于django的admin二次开发
simpleui:对django admin的美化
# django的admin自带rbac权限管理(表设计完成权限管理)---》6张表
用户表
角色表(组表,部门表)
权限表
角色和权限 >>> 多对多中间表
用户和角色 >>> 多对多中间表
django-admin中多了一张表:
用户对权限 >>> 多对多中间表
"""
在关系型数据库的关系中:只有三种---》本质只有一种:外键关系
一对多
多对多
一对一
"""
### 基于django的admin做二次开发,开发出公司内部的管理系统
1、纯基于原生
2、使用第三方美化:
xadmin(早就不维护了,弃坑了)
simpleui(国内的,主流),国外也有很多
django-admin演示
# 先创建超级用户
# 在models.py 创建表:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
author = models.CharField(max_length=32)
def __str__(self):
return self.name
class Meta:
verbose_name = '图书表'
verbose_name_plural = '图书表'
# 在admin.py中 注册表
from django.contrib import admin
from .models import Book
class BookAdmin(admin.ModelAdmin):
# 设置列表可显示的字段
list_display = ('name', 'author', 'price')
# 设置过滤选项
list_filter = ('name', 'price', )
admin.site.register(Book, BookAdmin)
# 可以修改配置文件为中文显示:
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
2、自动生成接口文档
# 顺利的写接口
在公司里,前端和后端是两拨人写---》咱们后端接口写好了,我们知道接口怎么用
# 后端人,需要写出接口文档,给前端用,前端按照接口文档去开发
# 具体格式可以参照:https://open.weibo.com/wiki/2/comments/show
# 如何写?
第一种:使用word或者md文档编写---》纯手写---》好的公司这么用
第二种:第三方平台录入---》半手写
https://blog.csdn.net/weixin_44337261/article/details/121005675-->部分公司
第三种:公司自己开发接口平台,搭建接口平台--》数据放在公司自己--》学长公司在用
https://zhuanlan.zhihu.com/p/366025001
第四种:自动生成接口文档---》用的少---》自动生成+导出---》录入到yapi
drf中有两个模块:coreapi,swagger
### coreapi使用
第一步:下载
pip3 install coreapi
第二步:修改路由
from rest_framework.documentation import include_docs_urls
# 接口文档
router.register('books2', views.BookListCreateView, 'books2') # 自动生成路由类的配置
urlpatterns = [
...
path('docs/', include_docs_urls(title='站点页面标题')),
]
第三步:写视图类,只需要加注释即可
from rest_framework.generics import ListCreateAPIView
class BookListCreateView(ViewSetMixin, ListCreateAPIView):
serializer_class = BookSerializer
queryset = Book.objects.all()
"""
get:
返回所有图书信息.
post:
新建图书.
"""
from rest_framework import mixins
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
"""
第四步:配置文件中
REST_FRAMEWORK = {
...
# 接口文档配置
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
3、jwt介绍和快速使用
# cookie,session,token的区别?
https://www.cnblogs.com/liuqingzheng/articles/8990027.html
# 认证:session机制:需要在后端存储数据
之前使用的django-session:
如果登录用户很多,需要在后端存很多数据,频繁查询数据库,导致效率低
能不能想一种方案,不在服务端存数据 在客户端存数据(数据安全)---》token认证机制
# Json web token (JWT),token是一种认证机制,用在web开发方向,叫 jwt
# JWT的构成
三段式---每一段都使用 base64编码
典型的jwt串样子,通过. 分隔成三段:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一段:头:声明类型,这里是jwt,声明加密的算法,公司信息等等(目前作用不大)
第二段:荷载(payload):有效信息
用户名,用户id,登陆时间,token失效时间...
第三段:签名(signature):通过 头+荷载 使用某种加密方式加密后得到的
## jwt的签发和认证--保证安全
# 签发
登陆过程 如果没有第三方模块帮助我们做,我们就自己做
1)用基本信息公司信息存储json字典,采用base64算法得到 头字符串
2)用关键信息存储json字典,采用base64算法得到 荷载字符串,过期时间,用户id,用户名
3)用头、体加密字符串通过加密算法+秘钥加密得到 签名字符串 拼接成token返回给前台
# 认证
访问需要登陆的接口
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间是安全信息,确保token没过期
3)再用 第一段 + 第二段 + 加密方式和秘钥得到一个加密串,与第三段 签名字符串 进行比较,通过后才能代表第二段校验得到的user对象就是合法的登录用户
# 大部分的web框架都会有第三方模块支持----》如果没有需要自己写
django中有一个django-rest-framework-jwt,咱们讲的:
https://github.com/jpadilla/django-rest-framework-jwt
django中还有一个,django-rest-framework-simplejwt,咱们不讲,公司可能会用:
https://github.com/jazzband/djangorestframework-simplejwt
# 区别
https://blog.csdn.net/lady_killer9/article/details/103075076
3.1、base64编码和解码
# base64编码和解码
只是编码和解码,不能叫加密
## 案例:
import base64
# 编码
# s = b'''{"name":"lqz","age":19}'''
# res = base64.b64encode(s)
# print(res) # eyJuYW1lIjoibHF6IiwiYWdlIjoxOX0=
# 解码
# s = b'eyJuYW1lIjoibHF6IiwiYWdlIjoxOX0='
# res = base64.b64decode(s)
# print(res) # {"name":"lqz","age":19}
### base64长度必须是4的倍数,如果不够,使用 = 补齐,所以base64编码,最后面可能有 =
### base64 用途在哪?
对网络传输中的数据进行编码---->jwt,有些图片,使用base64编码(12306)
3.2、django中快速使用jwt
3.2.1、签发
# 第一步:下载模块
pip3 install djangorestframework-jwt
# 第二步:在路由中配置
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
# jwt签发
path('login/', obtain_jwt_token),
]
# 第三步:使用接口测试工具发送post请求到后端,就能基于auth的user表签发token
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6InB5eSIsImV4cCI6MTY0OTMxNjc3MiwiZW1haWwiOiIzMDYzMzQ2NzhAcXEuY29tIn0.YOUc9gcFIQf9FBibZJANaI3Rmpw4zfMy1e8ez1roKSI"
}
# 比session优势在不需要在后端存数据了,jwt不会在后端存数据,保证了数据安全
### 会有被别人窃取的风险----》只能拿到和使用---》避免不了窃取后使用,只能避免别人篡改不了
爬虫就是干这事,扣除token串,模拟发请求----》如果你能想一种方式,避免别人获取了token串,不能发请求---》你就把整个爬虫行业干掉了
3.2.2、认证
# 视图类的某个方法,访问时候,需要认证通过才能访问
写认证类-->我们用了第三方模块---》第三方模块写了一个认证类
# 以获取所有图书接口为例
在视图类中配置:认证类+权限类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class BookView(GenericViewSet, ListModelMixin):
'''
返回所有图书信息
'''
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter]
search_fields = ['name', 'author'] # 过滤
pagination_class = CommonCursorPagination # 验证此类分页时 视图类中不可以带有排序功能 要注释掉
# JSONWebTokenAuthentication 就是 rest_framework_jwt模块写的认证类
authentication_classes = [JSONWebTokenAuthentication, ] # 登录认证
# 需要配合一个权限类
permission_classes = [IsAuthenticated, ]
### 在前端使用的时候,要携带token
token携带方式:在请求头中使用 Authorization : jwt token串
为什么要按这个格式?
人家的认证类已经写完了,就是去请求头中取的,按固定规则取的,所以咱们需要按照这个格式
4、jwt定制返回格式
# 签发token,其实就是jwt模块基于auth的user表,帮咱们写了一个登陆功能,但是一般请求,在我们登陆成功后,返回的数据更多
{code:100,msg:登陆成功,token:adfadf,username:pyy}
# 定义签发token(登陆接口)返回格式
第一步:写一个函数(新建 utils.py文件)--->返回什么格式,前端就能看到什么格式
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': "登陆成功",
'token': token,
'username': user.username
}
第二步:在配置文件中配置(不配置就会使用 JWT默认的格式)
# jwt模块的配置文件,统一放在JWT_AUTH
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}
5、jwt源码分析
# 签发源码
obtain_jwt_token---》ObtainJSONWebToken.as_view():视图类.as_view()
ObtainJSONWebToken视图类---》登陆post请求,携带用户名密码---》视图类中有post方法
# 视图类中的序列化类:
serializer_class = JSONWebTokenSerializer---》全局钩子中获取当前登录用户和签发token
# post方法中:
serializer = self.get_serializer(data=request.data)
### 你会的:
obtain_jwt_token---》ObtainJSONWebToken视图类---》post方法--》通过前端传入的用户名和密码拿到了当前用户,通过当前用户签发了token---》返回给了前端
# 认证源码
认证类---》authenticate方法---》验证
def authenticate(self, request):
jwt_value = self.get_jwt_value(request) # 获取真正的token,三段式
if jwt_value is None: # 如果没传token,就不认证了,直接通过,所以需要配合权限类一起用
return None
try:
payload = jwt_decode_handler(jwt_value) # 验证签名
except jwt.ExpiredSignature:
msg = _('Signature has expired.') # 过期了
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.') # 被篡改了
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed() # 不知名的错误
user = self.authenticate_credentials(payload)
return (user, jwt_value)