上篇文章我们刚讲完基于Session的JWT认证类 那我们现在研究研究怎样实现JWT的认证类 就从它的认证类下手JSONWebTokenAuthentication
点击进去发现没有authenticate
方法 但是它继承了BaseJSONWebTokenAuthentication
再进去看第一个就是我们要找的authenticate方法了
源码展示
class BaseJSONWebTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
jwt_value = self.get_jwt_value(request) # 获取前端传入的信息request
if jwt_value is None: # 判断是否有值
return None
try: # 最核心的就是JWT的核心代码了
payload = jwt_decode_handler(jwt_value) # 解码信息获取Token 去验证
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) # 返回当前登录用户 与token
我们看懂了这个源码之后我们就知道了怎样重写JWT认证类了 重写它的authenticate即可
class Base(BaseAuthentication):
def authenticate(self, request):
try:
rest_token = request.META.get('HTTP_AUTHORIZATION') # 获取传入的Token
jwt_name, token = rest_token.split(' ') # 取出token中的空格
payload = jwt_decode_handler(token) # 获取数据库的Token
except jwt.ExpiredSignature:
msg = ('签名时间已经过期啦!!!')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = ('错误的解放方式')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
user = UserInfo.objects.filter(pk=payload.get('user_id')).first()
return user, token
'''
也可以直接返回Payload就不用走数据库查询 加快效率
return (payload, token)
配置视图认证类
class JwtView(APIView):
authentication_classes = [Base, ]
'''
首先回想我们的排序与过滤 想使用他们两个必须继承GenericAPIView + ListModelMixin
及其子类 排序类OrderingFiler
只要在视图类中配置filter_backends、filterset_fields&search_fields
就可以实现过滤和排序了 内置的过滤类SearchFilter
Django-filter 自定义写一个类 继承BaseFilterBackend 重写filter_queryset 返回的queryset对象 就是过滤或排序后的数据了
再次我们使用排序与过滤的时候只会在获取信息的时候才会用到 所以我们进到源码中查找list方法就好了
def list(self, request, *args, **kwargs):
# self.get_queryset()所有数据,经过了self.filter_queryset返回了qs
# self.filter_queryset完成的过滤
queryset = self.filter_queryset(self.get_queryset())
# 如果有分页,走的分页----》视图类中配置了分页类
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
# 如果没有分页,走正常的序列化,返回
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
-self.filter_queryset完成了过滤,当前在视图类中,self是视图类的对象,去视图类中找没找到,去父类---》GenericAPIView---》filter_queryset
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
'''
-写的过滤类要重写filter_queryset,返回qs(过滤或排序后)对象
-后期如果不写过滤类,只要在视图类中重写filter_queryset,在里面实现过滤也可以
'''
什么是RBAC?
1. auth_user 用户表
2. auth_group 角色表
3. auth_permission 权限表
4. auth_user_groups 用户与角色中间表
5. auth_group_permissions 角色与权限中间表
6. auth_user_user_permissions 用户与权限中间表
快速体验Django Admin的RBAC的权限控制
创建好表模型
class UserInfo(models.Model):
username = models.CharField(max_length=32, verbose_name='名称')
password = models.CharField(max_length=32, verbose_name='密码')
class Meta:
verbose_name_plural = '用户信息表'
def __str__(self):
return self.username
通过createsuperuser进入后台新建用户以及其他信息 可以创建组表示这一组里面的角色有什么权限
ACL(Access Control List,访问控制列表)
将用户或组等使用者直接与对象的权限对接
比如张三只有看抖音发抖音的权限 没有开直播的权限 交给第三张中间表来控制权限授予用户的角色
RBAC(Role-Based Access Control,基于角色的访问控制)
将用户与角色对接 然后角色与对象的权限对接 就是跟Django的用法一样 本质就是RBAC+ACL 公司用的比较多
ABAC(Attribute-Based Access Control,基于属性的访问控制)
ABAC(Attribute-Based Access Control,基于属性的访问控制)又称为PBAC(Policy-Based Access Control,基于策略的访问控制)
CBAC(Claims-Based Access Control,基于声明的访问控制),传统的ACL RBAC的架构是 {subject action object},而ABAC的架构是
{subject action object contextual},且为他们添加了parameter(参数)----简单说就是给单个权限再加上属性双重认证的感觉
这个模块支持个个语言 专门做权限控制的模块(ACL RBAC ABAC) 方便我们快速做权限控制
安装Casbin
pip install casbin
根据casbin的要求需要创建两个文件一个model 一个policy 做一个最简单的ACL模型 就可以快速测试出有没有权限了
Casbin网站:https://docs.casbin.cn/zh/docs/supported-models
创建自定义py文件
import casbin
e = casbin.Enforcer("./model.conf", "./policy.csv")
sub = "alice" # 想要访问资源的用户
obj = "data1" # 将要被访问的资源
act = "read" # 用户对资源进行的操作
if e.enforce(sub, obj, act):
# 允许alice读取data1
print('有权限')
else:
# 拒绝请求,抛出异常
print('没有权限')
model.conf
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
policy.csv
p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin
Django-admin自带了权限控制 但是是前后端混合的 我们可以去二次开发 开发出公司内部的自动化运行 自动化测试 人事管理系统
但是他的界面样子UI不好看 所以我们现在就要对django-admin进行美化有两个主流 Xadmin(已过时) SimpleUI(正红)
或者是基于Drf+Vue 自己写前后端分离的权限管理 在Go源里面使用Go-Vue-Admin
安装
pip install django-simpleui
用pip或者源码方式安装simpleui后 在自己项目的settings.py文件中INSTALLED_APPS的第一行加入
INSTALLED_APPS = [
'simpleui', # 一定要注册在第一行!!!
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework',
]
然后这个时候访问我们的admin接口就会得到一个全新的admin系统 然后还可以进行美化修改主题以及字体大小…
SimpleUI新手快速上手指南网站:https://simpleui.72wo.com/docs/simpleui/quick.html#%E7%9B%AE%E5%BD%95
自定义左侧的菜单栏 settings.py
import time
SIMPLEUI_CONFIG = {
'system_keep': False,
'menu_display': ['Simpleui', '信息管理', '权限认证', '多级菜单测试', '动态菜单测试'], # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
'dynamic': True, # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
'menus': [{
'name': 'Simpleui',
'icon': 'fas fa-code',
'url': 'https://blog.csdn.net/MeiJin_',
# 浏览器新标签中打开
'newTab': True,
}, {
'app': 'app01',
'name': '信息管理',
'icon': 'fas fa-code',
'models': [
{
'name': '用户信息',
'url': 'app01/userinfo/',
'icon': 'far fa-surprise'
},
{
'name': '书籍信息',
'url': 'app01/books/',
'icon': 'far fa-surprise'
},
{
'name': '出版社信息',
'url': 'app01/publish/',
'icon': 'far fa-surprise'
},
]
},
{
'app': 'auth',
'name': '权限认证',
'icon': 'fas fa-user-shield',
'models': [{
'name': '用户',
'icon': 'fa fa-user',
'url': 'auth/user/'
}]
}, {
# 自2021.02.01+ 支持多级菜单,models 为子菜单名
'name': '多级菜单测试',
'icon': 'fa fa-file',
# 二级菜单
'models': [{
'name': 'Baidu',
'icon': 'far fa-surprise',
# 第三级菜单 ,
'models': [
{
'name': '爱奇艺',
'url': 'https://www.iqiyi.com/dianshiju/'
# 第四级就不支持了,element只支持了3级
}, {
'name': '百度问答',
'icon': 'far fa-surprise',
'url': 'https://zhidao.baidu.com/'
}
]
}, {
'name': '内网穿透',
'url': 'https://www.wezoz.com',
'icon': 'fab fa-github'
}]
}, {
'name': '动态菜单测试',
'icon': 'fa fa-desktop',
'models': [{
'name': time.time(),
'url': 'http://baidu.com',
'icon': 'far fa-surprise'
}]
}]
}
自定义按钮 admin.py
@admin.register(Books)
class BookAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'price', 'author') # 字段必须是模型中存在的字段
#
# 增加自定义按钮
actions = ['make_copy',] # 一定要与下方的函数名对称
def make_copy(self, request, queryset):
print('我是自定义的按钮') # 点击按钮要执行的事件
#
# # 显示的文本,与django admin一致
make_copy.short_description = '测试按钮'
# # icon,参考element-ui icon与https://fontawesome.com
make_copy.icon = 'fas fa-audio-description'
#
# # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
make_copy.type = 'danger'
#
# # 给按钮追加自定义的颜色
# custom_button.style = 'color:black;'
make_copy.confirm = '你是否执意要点击这个按钮?' # 弹窗提示按钮
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点点赞收藏+关注
谢谢支持 !!!