10.16 simpleui 集成监控大屏、restframework-jwt执行流程分析、jwt_xxx_handler

simpleui 集成监控大屏、restframework-jwt执行流程分析、jwt_xxx_handler

上节回顾

# 代码正常执行--->可以写一些类和方法--->正常代码在某个位置就会执行到我们写的类,方法
- 面向切面编程(AOP: 是oop的补充), spring框架使用了aop理念
- 装饰器:也是aop的实践

# 排序和过滤的源码分析
- 查询所有接口--->list--->queryset=self.filter_queryset(self.get_queryset())--->GenericAPIView的filter_queryset----》把配置在视图类上的所有过滤类一个个执行了一下返回了qs,把返回的qs序列化了

# jwt的认证类
- 写一个认证类,取出token,使用restframework-jwt提供的验证方法即可
- 拿到了payload--->要不要从数据库查出用户取决于需求

# ACL:访问控制列表
# RBAC:基于角色的访问控制
# RBAC+ACL:django的6个表
# ABAC:基于角色的访问控制+属性控制

# django的admin严格符合人ABC--->基于admin二次开发--->内部的管理系统
# 第三方美化:simpleui
# 分离版的rbac :vue+django
# simlieui的使用

一、simpleui 集成监控大屏

# 从gitee上找到开源的前端页面--->集成到项目中
https://gitee.com/lvyeyou/DaShuJuZhiDaPingZhanShi?_from=gitee_search
    
# 本质就是前后端混合的项目

二、restframework-jwt执行流程分析

# 签发的token,有过期额时间,过期时间是?
# 答:配置一般设为7天
自定义设置token的时间:
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

# 双token认证
- 用户正在app或者应用中操作,token突然过期,此时用户不得返回登录界面,重新进行一次登录,这种体验性不好,于是引入双token校验机制
- 实现原理:首次登录时服务端返回两个token,accessToken和refreshToken,accessToken过期时间比较短,refreshToken时间比较长,且每次使用后会刷新,每次刷新后的refreshToken都是不同

-- refreshToken假设7天,accessToken过期时间5分钟
-- 正常使用accessToken即可,如果accessToken过期了,重新发送请求,携带refreshToken过来,能正常返回,并且这次响应中又带了accessToken

# django中顶格写的代码,都会执行

1、签发

签发流程:本质就是登录接口--->【校验用户是否正确,如果正确签发token】写到了序列化类中,如果不正确返回错误

- obtain_jwt_token:核心代码--->ObtainJSONWebToken.as_view()
- ObtainJSONWebToken:视图类,实现了登录功能

class ObtainJSONWebToken(JSONWebTokenAPIView):
    serializer_class = JSONWebTokenSerializer

ObtainJSONWebToken继承了JSONWebTokenAPIView

class JSONWebTokenAPIView(APIView):
    # 局部禁用掉权限和认证
    permission_classes = ()
    authentication_classes = ()

    def get_serializer_context(self):
        return {
            'request': self.request,
            'view': self,
        }

    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__)
        return self.serializer_class

    def get_serializer(self, *args, **kwargs):
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)
	
    # 登录就是post请求,执行post方法
    def post(self, request, *args, **kwargs):
        # JSONWebTokenSerializer实例化得到一个序列化类的对象,传入前端的值
        serializer = self.get_serializer(data=request.data)
        
        # 校验前端传入的数据是否合法
        if serializer.is_valid():
            # 字段自己的规则、局部钩子,全局钩子(序列化类的validate方法)
            # 获取当前登录用户和签发token是在序列化类中完成的【以前是在视图类中完成的】
            
            # 序列化类里面返回的return值传给了serializer.object
            # 从序列化类对象中取出了当前登录用户
            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)

self.get_serializer(data=request.data)的get_serializer方法来自于JSONWebTokenSerializer类:

class JSONWebTokenSerializer(Serializer):
    def validate(self, attrs):  # attrs校验后的数据
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }

        if all(credentials.values()):
            # auth的校验用户名和密码是否正确
            user = authenticate(**credentials)

            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)
                # 通过用户获得 payload:{}
                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)

2、认证

class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    www_authenticate_realm = 'api'

    def get_jwt_value(self, request):
        # 获取用户传进来的token
        # get_authorization_header(request)根据请求头中HTTP_AUTHORIZATION,取出token
        # jwt token码
        # auth=['jwt','真正的token']
        auth = get_authorization_header(request).split()
        auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

        if not auth:  # 如果token不存在,先去cookie里面找下,不在直接返回None
            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
        
        # 判断auth切分后的列表的大小
        if len(auth) == 1:  # 如果auth列表只一个值说明少了
            msg = _('Invalid Authorization header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2: # 如果auth列表只一个值说明多了
            msg = _('Invalid Authorization header. Credentials string '
                    'should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        return auth[1]  # 返回token

    def authenticate_header(self, request):
        return '{0} realm="{1}"'.format(api_settings.JWT_AUTH_HEADER_PREFIX, self.www_authenticate_realm)  

父类中:BaseJSONWebTokenAuthentication--->authenticate--->class BaseJSONWebTokenAuthentication(BaseAuthentication);

我们需要重写的是authenticate,去父类中找。JSONWebTokenAuthentication继承了BaseJSONWebTokenAuthentication:

class BaseJSONWebTokenAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # jwt_value前端传入的token
        jwt_value = self.get_jwt_value(request)
        # 判断前端有没有传入token
        if jwt_value is None:
            return None  # 没有带token,认证类也能过,但是权限类会根据是否user判断能否通过权限类,所以使用jwt提供的认证类,必须配合权限类

        try:
            # jwt_decode_handler去BaseJSONWebTokenAuthentication找
            payload = jwt_decode_handler(jwt_value)  # 验证token,token合法,返回payload
        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)  # 通过payload得到当前登录用户

        return (user, jwt_value)  # 后期的request.user就是当前登录用户
    
注意:只要带了token,request.user就有值,如果没有带token,会继续往后走

get_jwt_value里面的get_authorization_header(request)(JSONWebTokenAuthentication继承BaseJSONWebTokenAuthentication)

def get_authorization_header(request):
    auth = request.META.get('HTTP_AUTHORIZATION', b'')
    if isinstance(auth, str):
        # Work around django test client oddness
        auth = auth.encode(HTTP_HEADER_ENCODING)
    return auth

3、jwt_encode_handler

def jwt_encode_handler(payload):
    key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload)
    return jwt.encode(
        payload,
        key,
        api_settings.JWT_ALGORITHM
    ).decode('utf-8')

def jwt_get_secret_key(payload=None):
    if api_settings.JWT_GET_USER_SECRET_KEY:
        User = get_user_model()  # 获取模型中的user类
        user = User.objects.get(pk=payload.get('user_id'))  # 根据表以及payload的用户id返回相匹配的对象
        key = str(api_settings.JWT_GET_USER_SECRET_KEY(user))  # 获取用户的密钥
        return key
    return api_settings.JWT_SECRET_KEY

4、jwt_decode_handler

def jwt_decode_handler(token):
    options = {
        'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
    }
    # get user from token, BEFORE verification, to get user secret key
    unverified_payload = jwt.decode(token, None, False)  # 将获取的token解码
    secret_key = jwt_get_secret_key(unverified_payload)  # 调用jwt_get_secret_key函数得到密钥
    return jwt.decode(
        token,
        api_settings.JWT_PUBLIC_KEY or secret_key,
        api_settings.JWT_VERIFY,
        options=options,
        leeway=api_settings.JWT_LEEWAY,
        audience=api_settings.JWT_AUDIENCE,
        issuer=api_settings.JWT_ISSUER,
        algorithms=[api_settings.JWT_ALGORITHM]
    )

5、jwt_payload_handler

def jwt_payload_handler(user):
    username_field = get_username_field()  
    username = get_username(user)  # 获取用户名

    warnings.warn(
        'The following fields will be removed in the future: '
        '`email` and `user_id`. ',
        DeprecationWarning
    )

    payload = {
        'user_id': user.pk,  # 用户的id
        'username': username,  # 用户名
        # 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA  # 当前时间加上300秒,设置的是过期的时间
    }
    if hasattr(user, 'email'):
        payload['email'] = user.email  # 如果用户有email添加到payload中
    if isinstance(user.pk, uuid.UUID):
        payload['user_id'] = str(user.pk)  # 如果用户id是uuid加密后,添加到payload中

    payload[username_field] = username

    # Include original issued at time for a brand new token,
    # to allow token refresh
    if api_settings.JWT_ALLOW_REFRESH:
        payload['orig_iat'] = timegm(
            datetime.utcnow().utctimetuple()
        )

    if api_settings.JWT_AUDIENCE is not None:
        payload['aud'] = api_settings.JWT_AUDIENCE

    if api_settings.JWT_ISSUER is not None:
        payload['iss'] = api_settings.JWT_ISSUER

    return payload

三、drf回顾

# djangorestframework
- 视图:执行流程多了东西
    -- 两个视图基类
    -- 5个视图扩张类
    -- 9个视图子类
    -- 视图集:ViewSetMinxin,ModelViewSet,ReadOnlyModelViewSet

- 路由
    -- 三种写法
        --- path('user/', views.UserView.as_view())
        --- 自动生成
        --- action装饰器

- 序列化类
    -- 字段类,字段属性(read_only, write_only)
    -- serializer:每个字段都要写
    -- ModelSerializer:跟表有映射关系
        --- model,fields,extra_kwargs

- 局部钩子,全局钩子

- save和update

- 自定义序列化的字段:两种方式

- 认证,权限,频率
    -- 认证类
    -- 权限类:ACL,RABC
    -- 频率类
    -- 局部使用和全局使用

- 过滤和排序
    -- 配置排序和过滤即可
    -- 内置的
    -- 第三方
    -- 自定义

- 全局异常
	-- 写函数,配置一下
 
- 接口文档
	-- md,word,第三方...

- jwt:
	-- 签发和认证
posted @ 2022-10-16 16:50  努力努力再努力~W  阅读(167)  评论(0)    收藏  举报