1. 程式人生 > 實用技巧 >DRF之JWT補充

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/[email protected]"
}

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)
    
    # 應用場景
    第一次查詢所有圖書,你通過多表聯查序列化之後的資料,直接快取起來
    後續,直接先去快取查,如果有直接返回,沒有,再去連表查,返回之前再快取