首页 > 编程语言 >drf_jwt源码执行流程-自定义用户表签发和认证-simpleui的使用-权限控制(acl,rbac)

drf_jwt源码执行流程-自定义用户表签发和认证-simpleui的使用-权限控制(acl,rbac)

时间:2023-02-12 19:33:34浏览次数:62  
标签:自定义 jwt auth acl --- token 源码 user 权限

目录

drf_jwt源码执行流程-自定义用户表签发和认证-simpleui的使用-权限控制(acl,rbac)

昨日内容回顾

# 1 接口文档的编写
	1 word,md 编写---> 存放位置:存放共享文件平台 git上
    2 第三方的接口文档编写平台
    3 公司自己开发 使用开源搭建 yapi
    4 自动生成接口文档
    	-django+drf:swagger,coreapi
        -FastAPI:自带自动生成接口文档
        
    
    -接口文档应该有的东西
    	-接口描述
        -请求地址
        -请求方式
        -请求编码格式
        -请求参数(get请求参数 post put请求参数)
        	-参数类型
            -参数是否必填
            -参数解释
        -返回数据
        	-json格式实例
            -重点参数解释
        -错误码:写到全局
        
        
# 2 cookie session token发展历史
	-会话保持
    	-cookie:存在与客户端浏览器的键值对
        -session:存在与服务器的键值对(可以存在文件内,内存,数据库...)
        -token:三部分:头 荷载 签名
        	-签发:登录
            -认证:登录后才能访问的接口
            	-认证类
                -中间件
                -装饰器
                -别的位置
                
                
# 3 jwt:json web token:前后端认证的机制 token的web形式认证机制

# 4 base64
	-jwt
    -前后端交互:字符串---> base64编码
    -图片:使用base64编码
    
# 5 django+drf如何使用jwt
	drf-jwt
    
# 6 快速签发
	配置一条路由---> 已经写了登录接口
    
# 7 定制返回格式
	写一个函数 返回什么 前端就看到什么
    
# 8 jwt的认证
	局部:视图类上
    	认证类:---> JSONWebTokenAuthentication
        权限类:drf IsAuthenticated
    全局
    
    携带token
    	请求头中
        	Authorization:jwt sadsfasdfasdf.asdfas'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300)df.asdfasdf
            
# jwt 配置问题
	记住的
    	-JWT_RESPONSE_PAYLOAD_HANDLER
        -'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300)
    了解的
    	'JWT_AUTH_HEADER_PREFIX': 'JWT',
        'JWT_SECRET_KEY': settings.SECRET_KEY,

今日概要

  • 1 drf-jwt源码执行流程(了解)

    • 1.1 签发(登录)
    • 1.2 认证(认证类)
  • 2 自定义用户表签发和认证

    • 2.1 签发
    • 2.2 认证
  • 3 simpleui的使用

    • 3.1 使用步骤
    • 3.2 大屏展示
  • 4 权限控制(acl,rbac)

  • 补充

今日详细

1 drf-jwt源码执行流程(了解)

1.1 签发(登录)

# 登录接口 路由匹配成功 执行obtain_jwt_token---> post请求---> ObtainJSONWebToken的post方法

	from rest_framework_jwt.views import obtain_jwt_token
	path('login/', obtain_jwt_token),
    

# ObtainJSONWebToken的post方法 继承APIView
    def post(self, request, *args, **kwargs):
        # 实例化得到序列化类
        serializer = self.get_serializer(data=request.data)
        # 做校验:字段自己 局部钩子 全局钩子
        if serializer.is_valid():
            # user:当前登录用户
            user = serializer.object.get('user') or request.user
            # 签发的token
            token = serializer.object.get('token')
            # 构造返回格式 咱们可以自定制---> 讲过
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            # 最终返回了咱们定制的返回格式
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
# 如何得到user 如何签发的token---> 在序列化类的全局钩子中得到user和签发的token
	JSONWebTokenSerializer---> 全局钩子---validate
	
    # 前端传入校验过后的数据---> {"username":"lqz","password":"lqz1e2345"}
    def validate(self, attrs):
        credentials = {
            # self.username_field: attrs.get(self.username_field),
            # 这句其实就相当于:就是拿前端传入的username和password
            'username':attrs.get('username')
            'password': attrs.get('password')
        }

        if all(credentials.values()):
            # auth模块:authenticate方法 可以传入用户名 密码---> 如果用户存在 就返回用户对象 如果不存在就是None
            # 正确的用户
            user = authenticate(**credentials)

            if user:
                # 校验用户是否为活跃用户 是否被禁用 禁用无法登录成功
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)
			   # 荷载:通过user得到荷载{id,name,email,exp}
                payload = jwt_payload_handler(user)

                return {
                    # jwt_encode_handler通过荷载得到token串
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)
            
            
 # 重点:
	1 通过user得到荷载:payload = jwt_payload_handler(user)
    2 通过荷载签发token:'token': jwt_encode_handler(payload)
    
'''
了解:
	翻译函数 只要做了国际化 放到英文 会翻译成该国语言(配置文件配置)
	from django.utils.translation import ugettext as _
	msg = _('Unable to log in with provided credentials.')
'''
'''
如下图:多了一层序列化类
'''

1.2 认证(认证类)

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# JSONWebTokenAuthentication---->父类BaseJSONWebTokenAuthentication----> authenticate方法

    def authenticate(self, request):
        # 前端带在请求头中的token值
        jwt_value = self.get_jwt_value(request)
        # 如果没有携带token 就不校验了
        if jwt_value is None:
            return None

        try:
            # jwt_value就是上边拿到的token
            # 通过token 得到荷载 中途出错就会抛异常
            # 出错的原因:
            	篡改token 签发过期 未知错误
            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()

        # 如果能顺利对应上 没有被异常捕获 说明token是可以信任的
        # payload就可以使用 通过payload得到当前登录用户
        user = self.authenticate_credentials(payload)

        # 返回当前登录用户和token
        return (user, jwt_value)
    
    
# jwt_value = self.get_jwt_value(request)
    def get_jwt_value(self, request):
        # 拿到了前端请求头中传入的jwt 头+荷载+签名
        # auth=[jwt, 头+荷载+签名]  split切割成列表
        auth = get_authorization_header(request).split()
        # 'jwt'
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:
            # 请求头中如果没带 去cookie中取
            if api_settings.JWT_AUTH_COOKIE:
                return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
            return None

        if smart_text(auth[0].lower()) != auth_header_prefix:
            return None

        if len(auth) == 1:
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        return auth[1]
    
# 认证类配置了 如果不传jwt 不会校验 所以需要配合权限类使用

2 自定义用户表签发和认证

2.1 签发

from django.shortcuts import render
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from .models import UserInfo
from rest_framework.response import Response

from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

# Create your views here.


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')
        user = UserInfo.objects.filter(username=username, password=password).first()
        if user:
            # 登录成功 签发token
            # 通过token得到payload
            payload = jwt_payload_handler(user)
            # 通过payload得到token
            token = jwt_encode_handler(payload)
            return Response({'code': 10000, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 10001, 'msg': '用户名或密码错误'})
        
        
# 自动生成路由
from rest_framework.routers import SimpleRouter
from app01.views import UserView

router = SimpleRouter()
router.register('user', UserView, 'user')

urlpatterns += router.urls

2.2 认证

from rest_framework.authentication import BaseAuthentication
from app01.models import UserInfo
import jwt
from rest_framework.exceptions import AuthenticationFailed

from rest_framework_jwt.settings import api_settings
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class JsonWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 取出token---> 请求头中 就叫token
        token = request.META.get('HTTP_TOKEN')
        if token:
            try:
                payload = jwt_decode_handler(token)
                # 得到当前用户
                user = UserInfo.objects.get(pk=payload.get('user_id'))
                # 只要访问一次需要登录的接口 就会去UserInfo表中查一次用户---> 优化
                # user=UserInfo(id=payload.get('user_id'),username=payload.get('username'))
                # user={'id':payload.get('user_id')}
                return user, token
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('token过期')
            except jwt.DecodeError:
                raise AuthenticationFailed('token认证失败')

            except jwt.InvalidTokenError:
                raise AuthenticationFailed('token无效')
            except Exception as e:
                raise AuthenticationFailed('未知异常')
        raise AuthenticationFailed('token没有传 认证失败')

3 simpleui的使用

# 在公司里做项目 要使用权限 要快速搭建后台管理 使用django的admin直接搭建 django的admin界面不好

# 第三方的美化
	xadmin:作者不更新了 使用bootstrap+jq
    simpleui:vue 界面更好看
    
# 现在阶段:一般前后端分离比较多:django+vue
	带权限的前后端分离的快速开发框架
    django-vue-admin
    自己写

3.1 使用步骤

# 使用教程:
https://simpleui.72wo.com/docs/simpleui/QUICK.html#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAdjango%E9%A1%B9%E7%9B%AE

# 1 安装
pip install django-simpleui

# 2 在app中注册
	注册在最上方	
    
# 3 内部app,图书管理系统某个链接要展示的字段---》在admin.py 中----》自定义按钮
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    list_display = ('nid', 'name', 'price', 'publish_date', 'publish')

    # 增加自定义按钮
    actions = ['custom_button']

    def custom_button(self, request, queryset):
        print(queryset)

    custom_button.confirm = '你是否执意要点击这个按钮?'
    # 显示的文本,与django admin一致
    custom_button.short_description = '测试按钮'
    # icon,参考element-ui icon与https://fontawesome.com
    # custom_button.icon = 'fas fa-audio-description'
    # # 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
    custom_button.type = 'danger'
    # # 给按钮追加自定义的颜色
    # custom_button.style = 'color:black;'
    
# 4 app名字显示中文,字段名字显示中文在app中设置
	-新增,查看修改展示中文,在表模型的字段上加:verbose_name='图书名字',help_text='这里填图书名'
	-app名字中文:apps.py---》verbose_name = '图书管理系统'
    
# 5 调整左侧导航栏----》 在settings内设置
	-menu_display对应menus name
    -如果是项目的app,就menus写app
    -菜单可以多级,一般咱们内部app,都是一级
    -可以增加除咱们app外的其它链接---》如果是外部链接,直接写地址,如果是内部链接,跟之前前后端混合项目一样的写法:咱们的案例---》show 的路由
SIMPLEUI_CONFIG = {
    'system_keep': False,
    'menu_display': ['图书管理', '权限认证', '张红测试'],  # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
    'dynamic': True,  # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
    'menus': [
        {
            'name': '图书管理',
            'app': 'app01',
            'icon': 'fas fa-code',
            'models': [
                {
                    'name': '图书',
                    'icon': 'fa fa-user',
                    'url': 'app01/book/'
                },
                {
                    'name': '出版社',
                    'icon': 'fa fa-user',
                    'url': 'app01/publish/'
                },
                {
                    'name': '作者',
                    'icon': 'fa fa-user',
                    'url': 'app01/author/'
                },
                {
                    'name': '作者详情',
                    'icon': 'fa fa-user',
                    'url': 'app01/authordetail/'
                },
            ]
        },
        {
            'app': 'auth',
            'name': '权限认证',
            'icon': 'fas fa-user-shield',
            'models': [
                {
                    'name': '用户',
                    'icon': 'fa fa-user',
                    'url': 'auth/user/'
                },
                {
                    'name': '组',
                    'icon': 'fa fa-user',
                    'url': 'auth/group/'
                },
            ]
        },
        {

            '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': '/show/',
                    'icon': 'fab fa-github'
                }]
        }
    ]
}


# 6 其它配置项
	SIMPLEUI_LOGIN_PARTICLES = False  #登录页面动态效果
    SIMPLEUI_LOGO = 'https://avatars2.githubusercontent.com/u/13655483?s=60&v=4'#图标替换
    SIMPLEUI_HOME_INFO = False  #首页右侧github提示
    SIMPLEUI_HOME_QUICK = False #快捷操作
    SIMPLEUI_HOME_ACTION = False # 动作

3.2 大屏展示

# 监控大屏展示
	-https://search.gitee.com/?skin=rec&type=repository&q=%E5%B1%95%E7%A4%BA%E5%A4%A7%E5%B1%8F
        
    -就是前后端混合项目,js,css,图片对应好,就可以了

权限控制(acl,rbac)

# 公司内部项目
	rbac:是基于角色的访问控制(Role-Based Access Control) 在rbac中 权限与角色相关联 用户通过成为适当角色的成员而得到这些角色的权限 这就极大简化了权限的管理 这样管理都是层级相互依赖的 权限赋予给角色 而把角色又赋予给用户 这样的权限设计很清楚 管理起来很方便
    
    -用户表:用户和角色多对多关系
    -角色表:
    	一个角色可以有多个权限
        	-开发:拉取代码,上传代码
            -财务:发工资,开人
    -权限表:角色和权限是多对多
    	-拉取代码
        -上传代码
        -发工资
        ...
        
    通过5张表完成rbac控制:用户表 角色表 权限表 用户角色中间表 角色权限中间表
    如果某个人 属于财务角色 单只想要拉取代码权限 不要上传代码权限---> 也可以直接赋予权限:
    	通过6张表:django的admin---> 后台管理就是使用这套权限认证
            用户表,
            角色表,
            权限表, 
            用户角色中间表, 
            角色权限中间表
            用户和权限中间表
            
# 互联网项目
	-acl:Access Control List 访问控制列表 权限放在列表中
    -权限:权限表---> 发视频 评论 开直播
    -用户表:用户和权限是一对多
    
    张三:[发视频,]
    李四:[发视频,评论,开直播]
    
    
# 演示了 django-admin 的权限控制
	-授予zpf 某个组
    -单独授予权限
    
# django -auth--6张表
	auth_user   用户表
	auth_group  角色表,组表
    auth_permission  权限表
    -----------
    auth_user_groups   用户和角色中间表
    auth_group_permissions  角色和权限中间表
	-------------
    auth_user_user_permissions  用户和权限中间表
    
    
'''
java:若依
go:gin-vue-admin
python:django-vue-admin
'''

'''
上海地区和语言修改:
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
'''

补充

# 1 django的auth user表 密码是加密的 即便同样的密码 密文都不一样
	每次加密 都随机生成一个盐 把盐拼到加密后的串中
# 比如
pbkdf2_sha256$150000$WJCkszhG1m9w$rb0LEWxMxLdifpIfeQhoZfjqOhPjwelH/YUsUMRXbSE=

明文:123
盐:WJCkszhG1m9w

后期来了明文123---> 去用户表中取秘钥---> 把秘钥的盐加上明文密码加密---> 如果加密之后和最后一段一样 则验证成功

# 2 自定义用户表 生成密码用密文
from django.contrib.auth.hashers import make_password

# 3 用户表的密码忘了怎么办
	新增一个用户 把他的密码复制过去
    
# 4 双token认证
	去自己查查看

标签:自定义,jwt,auth,acl,---,token,源码,user,权限
From: https://www.cnblogs.com/zpf1107/p/17114522.html

相关文章

  • 权限源码分析、认证源码分析、频率源码分析、异常处理、异常处理、jwt介绍和原理、drf
    权限源码分析写一个权限类局部使用配置在视图类的就会执行权限类的permission_classes方法完成权限校验'''drf的APIView源码执行流程: 1.先匹配路由APIView中as_vie......
  • DispatcherServlet源码解析
    DispatcherServlet中使用的默认策略接口DispatcherServlet.properties中指定了DispatcherServlet.java中使用的一些策略接口的默认实现:spring-webmvc-5.3.9.jar!/org/s......
  • Spring MVC自定义数据转换器
    SpringMVC自定义数据转换器数据转换器是指将客户端HTTP请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自护照设计转换的方式,HandlerAdapter已经提供了通......
  • [16] Zookeeper-源码2
    1.ZkServer选举源码1.1选举准备QuorumPeer@Overridepublicsynchronizedvoidstart(){if(!getView().containsKey(myid)){thrownewRuntimeEx......
  • Kubernetes CSI插件注册(一)—— node-driver-registrar源码分析
    1、概述node-driver-registrar是一个由官方K8ssig小组维护的辅助容器(sidecar),它的主要作用则是获取CSI插件信息并通过GRPC服务(即RegistrationServer)向Kubelet提供插件......
  • 89、商城业务---认证服务---自定义SpringSession完成子域session共享
    编写如下配置类即可(因为我们登录是在认证服务模块,而登录后跳转到首页是在商品服务模块,并且我们修改了session的名字,为了两个服务都可以取到该session,我们将该配置类在两个......
  • 9.Oracle绑定变量
    1.什么是绑定变量?通常在高并发的OLTP系统中,可能会出现这样的现象,单个SQL的写法、执行计划、性能都是没问题的,但整个系统的性能就是很差,这表现在当系统并发的数量增加......
  • Ribbon负载均衡 (源码分析)
    Ribbon负载均衡SpringCloud已经删除了ribbon组件,所以需要手动导入依赖。(要学是因为很多项目业务已经使用了ribbon)服务拉取的时候添加了@LoadBalanced注解,实现负载均衡......
  • StampedLock源码解析
    StampedLock源码解析StampedLock描述一种基于能力的锁,具有三种模式,用于控制读写访问。StampedLock的状态由版本和模式组成。锁获取方法返回一个表示并控制对锁状态访问......
  • [​DuckDB] 多核算子并行的源码解析
    DuckDB是近年来颇受关注的OLAP数据库,号称是OLAP领域的SQLite,以精巧简单,性能优异而著称。笔者前段时间在调研Doris的Pipeline的算子并行方案,而DuckDB基于论文《Morsel-Driv......