DRF之JWT补充

Posted surpass123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DRF之JWT补充相关的知识,希望对你有一定的参考价值。

DRF之JWT补充

1.JWT控制用户登录后才能反问,匿名用户无法访问

class QueryUserView(GenericViewSet, RetrieveModelMixin):
    """
    查询接口
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    pk = None
    # throttle_classes = [IPThrottle, ]
    authentication_classes = [LoginToken, ]
    permission_classes = [IsAuthenticated, ]

    def get_object(self):
        queryset = self.filter_queryset(self.get_queryset())
        filter_kwargs = {self.lookup_field: self.pk}
        return queryset.filter(**filter_kwargs)

    def get_queryset(self):
        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            queryset = queryset.all()
        return queryset.filter(is_delete=False)

    def retrieve(self, request, *args, **kwargs):
        self.pk = kwargs.get(‘pk‘)
        if not self.pk:
            return APIResponse(code=‘102‘, msg=‘查询失败‘, data={‘result‘: ‘缺少主键值‘})
        if not self.get_object():
            return APIResponse(code=‘102‘, msg=‘查询失败‘, data={‘result‘: ‘无效的主键值‘})
        instance = self.get_object().first()
        serializer = self.get_serializer(instance=instance)
        return APIResponse(code=‘102‘, msg=‘查询成功‘, data=serializer.data)

    def query(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

不携带token或者token失效无法登录成功

不携带token或者token失效,系统会默认你为匿名用户

技术图片

先进行登录,获取token值。携带正确的token可以登录成功

技术图片

将权限校验去掉后,即使携带正确的token也登录失败,因为系统此时默认你为匿名用户

2.控制登录接口返回数据的格式

方案一:自己写登录接口

方案二:用内置控制登录接口返回数据的格式

JWT配置信息中有这个属性
JWT_AUTH = {
    ‘JWT_RESPONSE_PAYLOAD_HANDLER‘:
    ‘rest_framework_jwt.utils.jwt_response_payload_handler‘,
}
# 重写jwt_response_payload_handler,然后进行替换

自定义基于JWT的权限认证

# 自定义基于jwt的权限认证

from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication

from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework_jwt.utils import jwt_encode_handler

import jwt

from app01 import models


class XJwtAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value = str(request.META.get(‘HTTP_AUTHORIZATION‘)) # 获取token信息
        if jwt_value:
            try:
                payload = jwt_decode_handler(jwt_value)      # 取出payload
            except jwt.ExpiredSignatureError:
                raise AuthenticationFailed(‘签名过期‘)
            except jwt.InvalidTokenError:
                raise AuthenticationFailed(‘非法用户‘)
            except Exception as e:
                raise AuthenticationFailed(str(e))
            """
            得到user对象:
            第一种方法去数据库查,
            第二种不查数据库
            """
            # user = models.User.object.get(pk=payload.get(‘user_id‘)) 第一种
            user = models.User(id=payload.get(‘user_id‘),username=payload.get(‘username‘))
            return user,jwt_value
        else:
            raise AuthenticationFailed(‘没有携带token认证信息‘)

jwt提供了通过三段token,取出payload的方法,并且有校验功能

class XJwtAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value=request.META.get(‘HTTP_AUTHORIZATION‘)
        if jwt_value:
            try:
            #jwt提供了通过三段token,取出payload的方法,并且有校验功能
                payload=jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed(‘签名过期‘)
            except jwt.InvalidTokenError:
                raise AuthenticationFailed(‘用户非法‘)
            except Exception as e:
                # 所有异常都会走到这
                raise AuthenticationFailed(str(e))
            user=self.authenticate_credentials(payload)
            return user,jwt_value
        # 没有值,直接抛异常
        raise AuthenticationFailed(‘您没有携带认证信息‘)

3.手动签发token来实现多方式的登录

# 使用用户名/手机号/邮箱都可以登录
# 前端需要传回来的json格式数据
{
    "username":"surpass/18395806407/18395806407@163.com"
}

LoginSerializer

from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
import re

class LoginSerializer(serializers.ModelSerializer):
    username = serializers.CharField(max_length=32)  # 覆盖username

    class Meta:
        model = models.User
        fields = [‘username‘, ‘password‘]

    def validate(self, attrs):
        username = attrs.get(‘username‘)
        password = attrs.get(‘password‘)
        if re.match(‘^1[3|4|5|7|8][0-9]{9}$‘, username):
            user = models.User.objects.filter(mobile=username)
        elif re.match(‘^.+@.+$‘, username):
            user = models.User.objects.filter(email=username)
        else:
            user = models.User.objects.filter(username=username)
        if not user:
            raise ValidationError(‘用户不存在‘)
        if not user.check_password(password):
            raise ValidationError(‘密码错误‘)
        payload = jwt_payload_handler(user)  # 传入user得到payload
        token = jwt_encode_handler(payload)  # 传入payload得到token 即jwt_value
        self.context[‘token‘] = token   # 利用context返回token和user
        self.context[‘user‘] = user
        return attrs

LoginView

class LoginView(GenericViewSet):
    queryset = User.objects.all()
    serializer_class = LoginSerializer

    def login(self, request, *args, **kwargs):
        login_ser = self.get_serializer(data=request.data, context={})
        login_ser.is_valid(raise_exception=True)
        token = login_ser.context.get(‘token‘)
        user = login_ser.context.get(‘user‘)
        return APIResponse(code=100, msg=‘登录成功‘, data={‘token‘: token, ‘username‘: user.username})

JWT的配置参数

# 使用内置的obtain_jwt_token时使用
JWT_AUTH = {
    ‘JWT_RESPONSE_PAYLOAD_HANDLER‘: ‘app01.utils.jwt_response_payload_handler‘,
    ‘JWT_EXPIRATION_DELTA‘: datetime.timedelta(days=7),  # 过期时间,手动配置
}

# utils.py
def jwt_response_payload_handler(token, user=None, request=None):
    return {
        ‘token‘: token,
        ‘msg‘: ‘登录成功‘,
        ‘status‘: 100,
        ‘username‘: user.username
    }

4.基于角色的权限控制(django内置的auth模块)

RBAC(Role-Based Access Control):基于角色的访问控制,通常用于公司的内部系统

# django的auth就是内置了一套关于RBAC的权限系统
核心是:通过角色绑定权限,然后给用户分配角色
在这种访问机制中,引入了role的概念,将用户与权限之间的关系进行了解耦,让用户通过角色与权限进行关联。在RBAC模型中,(who,what,how)构成了访问权限的三元组。

# 在django中
	# 后台的权限控制(公司内部系统,crm,erp,协同平台)
	user表
    permssion表
    group表
    user_groups表是user和group的中间表
    group_permissions表是group和permssion中间表
    user_user_permissions表是user和permission中间表
    # 前台(主站),需要用三大认证

5 .Django缓存

  • 前后端混合开发缓存的位置
# 缓存的位置,通过配置文件来操作(以文件为例子)
# 缓存的粒度:全站缓存,单页面缓存,以及页面局部缓存

页面局部缓存:

<body>
<div>
    {{ t }}<hr>
    {% load cache %}
    {% cache 5 ‘name‘ %}
        {{ time }}
    {% endcache %}
</div>
</body>

?单页面缓存:

# 给视图函数加上装饰器
from django.views.decorators.cache import cache_page


@cache_page(3)  # 缓存10秒中
def test2(request):
    import time
    time = time.time
    back_dict = {‘time‘: time}
    return render(request, ‘test2.html‘, back_dict)

全站缓存:

# 在中间件设置
MIDDLEWARE = [
    ‘django.middleware.cache.UpdateCacheMiddleware‘,
    ...
    ‘django.middleware.cache.FetchFromCacheMiddleware‘,
]
CACHE_MIDDLEWARE_SECONDS=10  # 全站缓存时间
  • 前后端分离项目缓存的使用

    # 使用步骤:
    # views.py
    from django.core.cache import cache
    
    def test1(request):
        import datetime
        cache.set(‘time‘,datetime.datetime.now())
        return render(request, ‘test1.html‘)
    
    
    def test2(request):
        time = cache.get(‘time‘)
        back_dict = {‘time‘:time}
        return render(request, ‘test2.html‘, back_dict)
    
    # 应用场景
    第一次查询所有图书,你通过多表联查序列化之后的数据,直接缓存起来
    后续,直接先去缓存查,如果有直接返回,没有,再去连表查,返回之前再缓存
    

以上是关于DRF之JWT补充的主要内容,如果未能解决你的问题,请参考以下文章

DRF之文档生成和JWT

我瞅瞅源码系列之---drf

drf-jwt第三方插件,DRF的三大认证的具体使用,多方式登陆的实现

DRF-jwt认证

drf框架中jwt认证,以及自定义jwt认证

在 DRF 中确定 IsAuthenticated 使用 DRF-JWT 检索使用 Postman 测试的列表