首页 > 其他分享 >drf-day12

drf-day12

时间:2023-09-12 21:23:21浏览次数:44  
标签:username jwt token user day12 msg payload drf

昨日回顾

基于自定义用户表签发token

1、前端(postman、web、appp、小程序)发送http请求,携带用户名和密码,通过中间件到达后端

2、后端request.data取出用户名和密码

3、拿着用户名和密码去数据库中查询,有没有

4、如果有就说明登陆成功

5、签发token:通过当前用户得到payload(自动生成荷载),通过荷载得到token

6、返回给前端(自定义)

7、查不到,就返回错误信息

基于auth的user表,签发token,多方式登录

1、扩写auth的user表:在配置文件中注册项目名.表名

2、在models文件中自己写一个表,继承Abstrcactuser

3、多方式登录

  前端接收 用户名,手机号,邮箱的这个字段是同一个
    {username:用户名/手机号/邮箱,password:密码}
  正则匹配,如果是手机号,就查 手机号+密码,如果是邮箱 邮箱+密码
  check_password
  签发token


  所有逻辑都写在视图类中
  逻辑写到序列化类中
  如果继承了ModelSerializer,必须重写username
  重写了全局钩子:在全局钩子中完成校验,如果校验不过,抛异常
  写了获取用户方法
  写了签发token方法
  把token和用户名放到了self.context中

基于自定义表编写认证类

补充

翻译函数
只要做国际化处理,就会显示当前国家的语言
from django.utils.translation import gettext_lazy as _
msg = _('Signature has expired.')
_是翻译函数的别名,只要做国际化处理,就会显示哪个国家的语言

认证类

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
import jwt
from rest_framework_jwt.settings import api_settings
from .models import User

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
from django.utils.translation import ugettext as _

class JWTLoginAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 1 取出用户传入的token----》带在哪里?后端规定的---》规定放在请求头 token=asfas.asfdas.asdfa
        token = request.META.get('HTTP_TOKEN')
        # 2 验证token-->django-jwt 提供了一个校验的方法---》 jwt_decode_handler 通过传入token传,校验,校验通过返回payload,校验失败抛异常
        try:
            payload = jwt_decode_handler(token)
            # 通过payload 得到当前用户
            # 认证类,配好后,只要有认证的,都会走这里---》每次走这里都会查询一次数据库
            # 做个优化?
            # 1 自己根据payload数据,创建一个用户,有的参数没传,会使用默认值,跟我真实用户还是有区别的
            # user=User(id=payload.get('user_id'),username=payload.get('username'))
            # 2 直接返回用户id,后续 的request.user 都是用户id,如果真正要用的时候,再查
            user = User.objects.get(pk=payload.get('user_id'))
        except jwt.ExpiredSignature as e:
            # msg = _('Signature has expired.') # _是个函数的别名,这个函数是翻译函数,只要做了国际化,它就是中文
            msg = '签名过期'
            raise AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = 'token错误'
            raise AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('不合法的token')
        except Exception:
            raise AuthenticationFailed('未知错误,请联系系统管理员')

        return (user, token)  # 后续的request.user 都是用户id,如果真正要用的时候,再查

    
    
优化:
 每次访问需要登录的接口,都会去查数据库
 所以咱们可以做优化
     1 自己生成一个user对象
     2 直接返回user_id,以后用的时候,再查数据库
 

 

登录接口

jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
# ### ### ### ### ##1 自己定义的用户表,签发token# ### ### ### ### ### ##
class UserView(ViewSet):
    @action(methods=['POST'],detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 签发token
            # 1 通过user生成payload---》jwt 提供一个方法(username),传入user,返回payload
            payload = jwt_payload_handler(user)
            # payload={'username':'asdfasdf','exp':1694401763}
            # 2 生成token---》jwt提供了方法,把payload放入--》token
            token = jwt_encode_handler(payload)
            return Response({'code': 101, 'msg': '登录成功', 'token': token, 'username': user.username})
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})

路由

outer.register('user', UserView, 'user') # 127.0.0.1:8000/user/login  的post请求
router.register('books', BooView, 'books') #127.0.0.1:8000/user/login  的post请求

 

视图类

from rest_framework.viewsets import GenericViewSet

# 登录后才能访问
from .auth import JWTLoginAuthentication
class BooView(GenericViewSet):
    authentication_classes = [JWTLoginAuthentication]#局部认证登录
    def list(self, request):
        return Response("你看到我了")

 

django-jwt源码分析

签发源码分析

# 1 入口是:obtain_jwt_token---》from rest_framework_jwt.views import obtain_jwt_token
# 2 obtain_jwt_token本质是:ObtainJSONWebToken.as_view()
# 3 看ObtainJSONWebToken视图类
    -前端post请求,提交了用户名密码,就能签发token
    -去ObtainJSONWebToken中找---》post
    
# 4 发现没有
    class ObtainJSONWebToken(JSONWebTokenAPIView):
        serializer_class = JSONWebTokenSerializer  #就是它
        
# 5 去父类中找JSONWebTokenAPIView
    
class JSONWebTokenAPIView(APIView):
    def post(self, request, *args, **kwargs):
        #serializer = self.get_serializer(data=request.data) # 配置的序列化类
        serializer =JSONWebTokenSerializer(data=request.data)
        if serializer.is_valid():  # 字段自己,局部,全局
            user = serializer.object.get('user')
            token = serializer.object.get('token')
            # 定制返回格式--》如果咱们写了,走自己的
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            return response
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
 # 6 校验用户名密码和生成token,写在了序列化类的--》全局钩子validate中--》JSONWebTokenSerializer

class JSONWebTokenSerializer(Serializer):
    def validate(self, attrs):
        credentials = {
            'username': attrs.get("username"),
            'password': attrs.get('password')
        }
        if all(credentials.values()): # credentials字典的value必须不能为空
            # authenticate django的auth,通过用户名和明文密码校验用户是否存的函数:authenticate
            # User.objects.filter(username='lqz',password='123').first()
            # user = authenticate(username='lqz',password='123')
            user = authenticate(**credentials)
            if user: # 登录成功,前token
                if not user.is_active: # 判断用户是否锁定
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    '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)

签发总结

 前端携带用户名密码到后端,执行后端你的post方法,后端生成一个序列化类的对象,执行校验,走了全局钩子,全局钩子中通过用户名密码获取用户(如果获取不到抛异常),获取到后签发token,签发完返回,在视图类中,取出来,返回给前端

 

认证源码分析

# 1 从哪开始看---》认证类JSONWebTokenAuthentication
# 2 JSONWebTokenAuthentication 的 
class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def get_jwt_value(self, request):
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:
            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]
    
    
# 2 父类中BaseJSONWebTokenAuthentication的authenticate
    def authenticate(self, request):
        # 拿出前端传入的token,可能前端没传,就是None
        jwt_value = self.get_jwt_value(request)
        if jwt_value is None: # None就不管了,认证类继续往后走,相当于没有认证,于是咱们才需要权限类
            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)
    
    
   

 

权限介绍

# 所有项目都会有权限控制
https://www.cnblogs.com/liuqingzheng/articles/16972464.html


ACL(Access Control List,访问控制列表)---》针对互联网用户,多半是这个
    将用户与权限对接(多对多)
       张三   [发视频,点赞,评论]
       李四   [看视频]
    
    
    
RBAC(Role-Based Access Control,基于角色的访问控制)--》公司内部项目
    将用户与角色对接,然后角色与对象的权限对接。
    django的admin+auth就是使用了这套认证机制
    
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(参数)。

    subject属性:比如用户的年龄、部门、角色、威望、积分等主题属性。

    action属性:比如查看、读取、编辑、删除等行为属性。

    object属性:比如银行账户、文章、评论等对象或资源属性。

    contextual属性:比如时段、IP位置、天气等环境属性

 

实际应用

# python写公司内部项目比较多,使用rbac控制居多
    RBAC  是基于角色的访问控制(Role-Based Access Control )在 RBAC  中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
    
    
    
# rbac如何设计
    用户表》》》一堆用户:张三,李四
    角色表(group组)》》》用户的角色:后勤部,开发部,总裁办
    权限表》》》放了一堆权限:发电脑,提交代码,删除代码,发工资
    用户和角色多对多:一个用户属于多个角色,一个角色属于多个用户
    角色 和权限多对多:一个角色有多个权限,一个权限属于多个角色
    rabc通过5张表可以实现
    django为了更细粒度划分》》》多了一张表,用户和权限多对多

 

演示django的rbac权限控制

 

 

simpleui的使用

公司内部,做公司内的项目需要使用这套权限控制
方案一:使用django-admin写
有的公司,不怎么写前端,直接使用django的admin,快速写出一套具有权限管理的系统
django admin的界面不好看:第三方美化》》》simpleui

方案二:自己写,前端使用vue,后端使用django,做公司内部的项目
    第三方开源的权限控制 项目
        python界:django-vue-admin   7期学长写的
        java界:若依
        go界:gin-vue-admin

        
 django admin的美化: simpleui

 

标签:username,jwt,token,user,day12,msg,payload,drf
From: https://www.cnblogs.com/shanghaipudong/p/17696251.html

相关文章

  • drf————源码分析
    drf————源码分析>认证源码分析权限源码分析频率类源码分析三大认证的源码分析之前读取的APIView的源码的执行流程中包装了新的request,执行了三大认证,执行视图类的方法,处理了全局异常查看源码的入口APIView的dispatch进入后在APIView的dispatch的496行上下self.......
  • drf-jwt自定义表签发、多方式登录
    一、jwt自定义表签发自定义表签发,用的是自己定义的表1.models.py:-注意点:因为视图中使用了drf-jwt的自动签发,所以用户名必须为usernamefromdjango.dbimportmodels#自定义签发用的是自定义的user表#注意点:使用drf-jwt的自动签发,用户名必须是usernameclassUser(......
  • drf-day11
    jwt自定义表签发models.pyfromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportAbstractUser#继承AbstractUser直接使用自动签发token#纯自己写的用户表,需要自己签发classUser(models.Model):username=models.CharField(max_length=32)......
  • drf - 过滤、排序、异常源码剖析、jwt
    过滤类的源码剖析1、为什么在视图类中配置了一个过滤类,就可以走? -filter_backends=[SearchFilter,MyFilter]2、前提条件是必须继承在视图类中继承GenericAPIView: 因为filter_backends是GenericAPIView的类属性。3、如果光继承了GenericAPIView还是不行,还需要再继承List......
  • drf- 过滤、排序、异常处理
    session的执行流程写一个登录接口----->保存用户的登录状态 -获取到用户名,密码-使用request.session["username"]=用户名、或者request.session["pk"]=pk值-签发阶段做了三件事: -1、生成一个随机的字符串-2、在django_session表中保存 -se......
  • drf- 三大认证、排序
    三大认证组件登录认证我们可以使用auth自带的User表,也可以选择自定义表.写登录接口,登录成功要有标志,生成一个随机的字符串,放到表中,以后只要携带这个字符串就可以登录成功。视图层方法一:classUserView(GenericViewSet):queryset=models.UserInfo.objects.all()......
  • 接口文档、jwt介绍和构成、jwt签发和认证、base64编码、drf-jwt编码、drf-jwt使用
    接口文档作为后端,接口写好了,需要编写接口文档作为前端,需要使用后端写的接口(移动端、web桌面端),可以照着接口文档写接口文档的展现形式:1.word、md,写好传到公司的某个平台===》前端可以下载使用2.自动生成接口文档===》后端通过配置===》把所写的接口都自动生成===》......
  • 接口文档,jwt介绍和构成,jwt签发与认证,base64编码,drf-jwt使用,django-rest-framewor
    1接口文档#作为后端,接口写好了#作为前端,需要使用我们写的接口(移动端,web,桌面端)#后端需要写接口文档#接口文档的展现形式: 1word,md,写好传到公司的某个平台---》前端可以下载2自动生成接口文档---》后端通过配置--》把所写的接口都自动生成---》地址--》访问......
  • drf-jwt使用
    一、jwt介绍和构成1.介绍jwt:JsonWebToken,Web方向的Token认证方案在用户注册或登录之后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证(token串)。我们不再使用session认证机制,而使用JsonWebToken(本质就是token)认证机制。JsonWebToken:JWT用在我们前后端......
  • drf-day10
    接口文档解析图 接口文档展现形式作为后端,接口写好,还需要写接口文档作为前端,需要使用我们写的接口(移动端、web、桌面端)接口文档的展现形式:1.word、md,写好传到公司的某个平台,前端可以下载2.自动生成接口文档,后端通过配置,把所写的接口都自动生成一个地址,访问这个地址能看......