首页 > 其他分享 >drf从入门到精通 10

drf从入门到精通 10

时间:2023-02-10 21:33:19浏览次数:44  
标签:10 入门 jwt fa token user 权限 payload drf

今日内容详细

1.drf-jwt源码执行流程

1.1签发(登录)

# 登录接口,路由匹配成功,执行obtain_jwt_token---> post请求---> ObtainJSONWebToken的post方法
	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":"qyf","password":"123"}
		def validate(self, attrs):
			credentials = {
			self.username_field: attrs.get(self.username_field),
			'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:jwt_encode_handler(payload)
    
## 了解
	翻译函数 只要做了国际化 放的英文 会翻译成该国语言(配置文件配置的)
	from django.utils.translation import ugettext as _
	msg = _('Unable to log in with provided credentials.')

image-20230210102854572

1.2认证(认证类)

# 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,签名]
		        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 rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import UserInfo
from .authentication import JsonWebTokenAuthentication


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
            # 通过user得到payload
            payload = jwt_payload_handler(user)
            # 通过payload得到token
            token = jwt_encode_handler(payload)
            return Response({'code': 1000, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 1001, 'msg': '用户名或密码错误'})

2.2认证

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from .models import UserInfo
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使用步骤

# 1 安装
	pip install django-simpleui
    
# 2 在app中注册

# 3 调整左侧导航栏----》
	-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/publisssh/'
                },
                {
                    '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'
                }]
        }
    ]
}
	
    
    
# 4 内部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;'
    
    
    
# 5 app名字显示中文,字段名字显示中文
	-新增,查看修改展示中文,在表模型的字段上加:verbose_name='图书名字',help_text='这里填图书名'
	-app名字中文:apps.py---》verbose_name = '图书管理系统'

    
# 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,图片对应好,就可以了

4.权限控制(acl,rbac)

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

标签:10,入门,jwt,fa,token,user,权限,payload,drf
From: https://www.cnblogs.com/qian-yf/p/17110343.html

相关文章

  • python之路68 drf从入门到成神 9 drf_jwt源码执行流程、自定义用户表签发和认证、simp
    drf-jwt源码执行流程(了解)签发(登录)源码分析登录接口,路由匹配成功,执行obtain_jwt_token----》post请求----》ObtainJSONWebToken的post方法path('login/',obtain_jwt......
  • drf
    今日内容1.drf-jwt源码执行流程(了解)2.自定义用户表签发和认证3.simpleui的使用4.权限控制(acl,rbac)1.drf-jwt源码执行流程(了解)签发登录接口,路由匹配成功,执行ob......
  • 《Terraform 101 从入门到实践》 第四章 States状态管理
    《Terraform101从入门到实践》这本小册在南瓜慢说官方网站和GitHub两个地方同步更新,书中的示例代码也是放在GitHub上,方便大家参考查看。军书十二卷,卷卷有爷名。为......
  • 学习打卡01- java入门
    1,基础知识点:java的三个版本javaSE(java基础版),javaEE(java企业版),javaME(小型嵌入式开发)LTS(Longterm<时期>support<支持>)长期支持版公司长期维护包括5.......
  • 738~739 Cookie快速入门,原理分析
    会话技术1.会话:一次会话中包含多次请求和响应一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到一方断开为止2.功能:在一次会话的范围内的多次请求......
  • 入门级1
    #include<iostream>#include<string>usingnamespacestd;intmain(){ intn; cin>>n; intzi=0; stringstr; for(inti=0;i<n;i++){ cin>>str; for(int......
  • 2023年河南省青少年程序设计能力认证(入门组)小豫豫在郑州
    #include<bits/stdc++.h>usingnamespacestd;intn;intshu=0;intmain(intargc,char**argv){cin>>n;stringshou;stringstr;fo......
  • 2周刷完100道前端优质面试真题
    第1章课程简介第2章前端面试技能拼图1:数据结构和算法(上),大厂面试必考第3章前端面试技能拼图1:数据结构和算法(下),大厂面试必考第4章前端面试技能拼图2:前端基础知识,......
  • [leetcode每日一题]2.10
    ​​1223.掷骰子模拟​​难度困难166有一个骰子模拟器会每次投掷的时候生成一个1到6的随机数。不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字 ​​i​......
  • drf-jwt源码执行流程、自定义用户表签发和认证、simpleui的使用、 权限控制(acl,rbac)
    1drf-jwt源码执行流程1.1签发(登录)#登录接口,路由匹配成功,执行obtain_jwt_token---》post请求---》ObtainJSONWebToken的post方法 path('login/',obtain_jwt_token)......