• jwt token认证


    1、drf-jwt手动签发与校验

    2、drf小组件:过滤、筛选、排序、分页 => 针对与群查接口

    jwt_token源码分析(入口)

    rest_framework_jwt --> views.py --> ObtainJSONWebToken(JSONWebTokenAPIView)
    
    class ObtainJSONWebToken(JSONWebTokenAPIView):
        serializer_class = JSONWebTokenSerializer
    
    然后到父类中JSONWebTokenAPIView的post方法
        def post(self, request, *args, **kwargs):
            #从get_serializer获取serializer
            serializer = self.get_serializer(data=request.data)
    		........
    点击get_serializer
       def get_serializer(self, *args, **kwargs):
    		#获取到serializer_class类
            serializer_class = self.get_serializer_class()
            kwargs['context'] = self.get_serializer_context()
            return serializer_class(*args, **kwargs)
    
    回到
       def post(self, request, *args, **kwargs):
            # 这里就是将数据传入,然后反序列化做校验
            serializer = self.get_serializer(data=request.data)
    
            if serializer.is_valid():
                user = serializer.object.get('user') or request.user
                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)
    
     然后找到jwt的资源文件serializers.py中的JSONWebTokenSerializer(Serializer)
    class JSONWebTokenSerializer(Serializer):
       
        def __init__(self, *args, **kwargs):
            """
            Dynamically add the USERNAME_FIELD to self.fields.
            """
            super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)
    		#username_field是活的,而‘password’是死的
            self.fields[self.username_field] = serializers.CharField()
            #write_only=True说明只能是反序列化
            self.fields['password'] = PasswordField(write_only=True)
    
    点击PasswordField类
    class PasswordField(serializers.CharField):
    
        def __init__(self, *args, **kwargs):
            if 'style' not in kwargs:
                kwargs['style'] = {'input_type': 'password'}
            else:
                kwargs['style']['input_type'] = 'password'
            super(PasswordField, self).__init__(*args, **kwargs)
    
    当我们发向接口发送post请求就要携带username和password字段,不然会报错,因为在反序列化的时候校验的只是这两个字段,所以说这两个字段是必填,然后返回token
    

    value :记得加jwt空格+token

    签发token源码分析

    #通过账号密码签发token(依赖auth组件的RBAC用户权限六表)
    class ObtainJSONWebToken(JSONWebTokenAPIView):
        """
        API View that receives a POST with a user's username and password.
    
        Returns a JSON Web Token that can be used for authenticated requests.
        """
        serializer_class = JSONWebTokenSerializer
        
        
    点击JSONWebTokenAPIView,post方法是签发token
       def post(self, request, *args, **kwargs):
            #传入data进行反序列化校验
            serializer = self.get_serializer(data=request.data)
    		#如果校验成功返回response
            if serializer.is_valid():
                #从序列化对象object是个字典,这个字典存放着user,token,就是这一步serializer = self.get_serializer(data=request.data)将数据给序列化类,这个类再通过username,password产生了用户并且产生了token,通过这个对象获取已经处理好的user和token
                
                #总结:获取user与签发token都是序列化类完成的
                
                #获取用户
                user = serializer.object.get('user') or request.user
                #获取token
                token = serializer.object.get('token')
                #根据token,user,request封装成要被返回的数据
                response_data = jwt_response_payload_handler(token, user, request)
                #将数据做初始化得到response对象
                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)
                    #返回response对象
                return response
    		#校验失败返回错误信息
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    
    查看序列化类资源文件serializers.py中的JSONWebTokenSerializer
    class JSONWebTokenSerializer(Serializer):
        """
        Serializer class used to validate a username and password.
    
        'username' is identified by the custom UserModel.USERNAME_FIELD.
    
        Returns a JSON Web Token that can be used to authenticate later calls.
        """
        def __init__(self, *args, **kwargs):
            """
            Dynamically add the USERNAME_FIELD to self.fields.
            """
            super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)
    
            self.fields[self.username_field] = serializers.CharField()
            self.fields['password'] = PasswordField(write_only=True)
    
        @property
        def username_field(self):
            return get_username_field()
    

    想要进入到super中在is_valid()之前,也就是serializer这一步进行操作,不能下一步到is_valid()这一步进入就会没有数据,因为是再校验之前做的实例化

    class JSONWebTokenSerializer(Serializer):
    
        def __init__(self, *args, **kwargs):
            """
            Dynamically add the USERNAME_FIELD to self.fields.
            """
            super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)
    
            self.fields[self.username_field] = serializers.CharField()
            self.fields['password'] = PasswordField(write_only=True)
    
        @property
        def username_field(self):
            return get_username_field()
    	
        ##重点看这个校验函数方法
        #全局钩子,就是post方法serializer.is_valid():做校验的时候就会走全局钩子校验
        def validate(self, attrs):
            #获取用户和密码
            credentials = {
                self.username_field: attrs.get(self.username_field),
                'password': attrs.get('password')
            }
    
            if all(credentials.values()):
                #调用authenticate,就是将账号密码传入做认证,得到用户
                user = authenticate(**credentials)
    
                if user:
                    #判断用户是否是活跃用户
                    if not user.is_active:
                        msg = _('User account is disabled.')
                        raise serializers.ValidationError(msg)
    				#根据用户产生payload对象,也就是用户的载荷,可以对外展示的信息,如用户的id,用户名,电话,过期时间等,但是没有密码,如果需要获取到用户的密码可以根据用户id来映射出用户密码
                    payload = jwt_payload_handler(user)
    				#返回有user和token的字典
                    return {
                        #传入payload产生签名
                        '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)
    
       def post(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
    
            if serializer.is_valid():
                #校验通过就说明可以获取到用户和token,所以说user和token在全局钩子里已经产生了,在序列化类中就已经解析出user和password了,所以可以通过序列化对象获取到user和token
                user = serializer.object.get('user') or request.user
                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)
    
    

    校验token源码分析

    发送请求

    urls.py
    from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token
    from django.urls import re_path
    from api import views
    urlpatterns = [
        re_path(r'^jogin/$', obtain_jwt_token),
    
    re_path(r'^user/detail/$', views.UserDetail.as_view()),
    
    ]
    
    views.py
    from rest_framework.views import APIView
    from utils.response import APIResponse
    # 必须登录后才能访问 - 通过了认证权限组件
    from rest_framework.permissions import IsAuthenticated
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    class UserDetail(APIView):
        #请求过来走JSONWebTokenAuthentication
        authentication_classes = [JSONWebTokenAuthentication]  # jwt-token校验request.user
        #IsAuthenticated这个认证是判断request.user是否有值
        permission_classes = [IsAuthenticated]  # 结合权限组件筛选掉游客
        def get(self, request, *args, **kwargs):
            return APIResponse(results={'username': request.user.username})
    
       
    
    走JSONWebTokenAuthentication,这个类没有authenticate认证方法,所以走父类BaseJSONWebTokenAuthentication
    class BaseJSONWebTokenAuthentication(BaseAuthentication):
        """
        Token based authentication using the JSON Web Token standard.
        """
    
        def authenticate(self, request):
            """
            Returns a two-tuple of `User` and token if a valid signature has been
            supplied using JWT-based authentication.  Otherwise returns `None`.
            """
            #传过来的authentication是带着请求头jwt,但是这里获取出来的没有带jwt
            #没有认证,代表游客,返回none
            jwt_value = self.get_jwt_value(request)
            if jwt_value is None:
                return None
    		#进行一通校验
            try:
                #通过token得到payload
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                #token过期捕获异常
                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()
    		#通过payload来获取用户,点击authenticate_credentials  username = jwt_get_username_from_payload(payload)
            #如果校验通过拿到user
            user = self.authenticate_credentials(payload)
    		#返回user
            return (user, jwt_value)
        
    
    

    点击get_jwt_value

    点击get_authorization_header()

    想要获取到authenticate_credentials就可以views.py视图类中继承BaseJSONWebTokenAuthentication, authenticate_credentials是BaseJSONWebTokenAuthentication的方法

    手动签发token

    # 1)通过username、password得到user对象
    # 2)通过user对象生成payload:jwt_payload_handler(user) => payload
    #		from rest_framework_jwt.serializers import jwt_payload_handler
    # 3)通过payload签发token:jwt_encode_handler(payload) => token
    #		from rest_framework_jwt.serializers import jwt_encode_handler
    
    签发token的请求 - 登录请求 - ObtainJSONWebToken - post - 将账号密码丢给序列化类处理 - 得到user、token => 序列化类的全局钩子
    

    签发源码小总结:

    # 前提:给一个局部禁用了所有 认证与权限 的视图类发送用户信息得到token,其实就是登录接口
    
    # 1)rest_framework_jwt.views.ObtainJSONWebToken 的 父类 JSONWebTokenAPIView 的 post 方法
    #		接受有username、password的post请求
    # 2)post方法将请求数据交给 rest_framework_jwt.serializer.JSONWebTokenSerializer 处理
    #		完成数据的校验,会走序列化类的 全局钩子校验规则,校验得到登录用户并签发token存储在序列化对象中
    
    
    
    自己的理解:
    (只需要一条路由就可以签发token,视图类都不需要写)
    携带json格式的用户密码超url发送post请求,url: obtain_jwt_token也就是ObtainJSONWebToken.as_view()方法 , ObtainJSONWebToken没有as_view(),到父类中JSONWebTokenAPIView中也没有,到JSONWebTokenAPIView的APIView中找到as_view并且返回调用view函数的返回值,view函数中dispatch方法,就是请求分发,所以post请求来到ObtainJSONWebToken没有,到父类JSONWebTokenAPIView找到,只写了这个post方法,所以get方法不行,serializer = self.get_serializer(data=request.data)调用ObtainJSONWebToken的对象调用get_serializer将data进去进行反序列化,get_serializer(data=request.data),get_serializer这个方法传入数据,是调用get_serializer中的 self.get_serializer_class()找到的serializer_class,serializer_class是在ObtainJSONWebToken中找到 serializer_class = JSONWebTokenSerializer,然后将JSONWebTokenSerializer(data=request.data)传入进行反序列操作,所以走JSONWebTokenSerializer的__init__方法,调用父类序列化,然后对传入的账号密码声明,校验走 validate方法,根据用户产生payload对象,也就是用户的载荷,可以对外展示的信息,如用户的id,用户名,电话,过期时间等,但是没有密码,如果需要获取到用户的密码可以根据用户id来映射出用户密码
                    payload = jwt_payload_handler(user)
    				#返回有user和token的字典
                    return {
                        #传入payload产生签名
                        'token': jwt_encode_handler(payload),
                        'user': user
                    }
    
    核心源码:rest_framework_jwt.serializer.JSONWebTokenSerializer的validate(self, attrs)方法
    def validate(self, attrs):
        # 账号密码字典
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }
        if all(credentials.values()):
            # 签发token第1步:用账号密码得到user对象
            user = authenticate(**credentials)
            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)
                # 签发token第2步:通过user得到payload,payload包含着用户信息与过期时间
                payload = jwt_payload_handler(user)
                # 在视图类中,可以通过 序列化对象.object.get('user'或者'token') 拿到user和token 
                return {
                    # 签发token第3步:通过payload签发出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)
    

    手动校验token

    # 1)从请求头中获取token
    # 2)根据token解析出payload:jwt_decode_handler(token) => payloay
    #		from rest_framework_jwt.authentication import jwt_decode_handler
    # 3)根据payload解析出user:self.authenticate_credentials(payload) => user
    #		继承drf-jwt的BaseJSONWebTokenAuthentication,拿到父级的authenticate_credentials方法
    

    校验源码小总结

    # 前提:访问一个配置了jwt认证规则的视图类,就需要提交认证字符串token,在认证类中完成token的校验
    
    # 1)rest_framework_jwt.authentication.JSONWebTokenAuthentication 的 父类 BaseJSONWebTokenAuthentication 的 authenticate 方法
    #		请求头拿认证信息jwt-token => 通过反爬小规则确定有用的token => payload => user
    
    核心源码:rest_framework_jwt.authentication.BaseJSONWebTokenAuthentication的authenticate(self, request)方法
    def authenticate(self, request):
        """
        Returns a two-tuple of `User` and token if a valid signature has been
        supplied using JWT-based authentication.  Otherwise returns `None`.
        """
        # 带有反爬小规则的获取token:前台必须按 "jwt token字符串" 方式提交
        # 校验user第1步:从请求头 HTTP_AUTHORIZATION 中拿token,并提取
        jwt_value = self.get_jwt_value(request)
        # 游客
        if jwt_value is None:
            return None
    	# 校验
        try:
            # 校验user第2步:token => payload
            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第3步:token => payload
        user = self.authenticate_credentials(payload)
    
        return (user, jwt_value)
    

    代码详见:

    E: en_djangopage

  • 相关阅读:
    只要有梦想
    过去这一年
    Importing BizTalk Applications to Production Environment
    SQL Server 2005 – Database Master Key
    Check Page Rank of any web site pages instantly
    An Introduction to SQL Server Service Broker [WORD DOCUMENT]
    Svcutil.exe – Generate the proxy class for the WCF client application
    SQL Server 2005: how to add a linked server
    BizTalk Error: The published message could not be routed
    Enable routing for failed messages in BizTalk 2006
  • 原文地址:https://www.cnblogs.com/huangxuanya/p/11734976.html
Copyright © 2020-2023  润新知