三大認證使用及異常處理

三大認證使用及異常處理

一、系統權限類使用

圖書接口:遊客只讀,用戶可增刪改權限使用前端

from rest_framework.permissions import IsAuthenticatedOrReadOnly
# ModelViewset便可增刪改查
class BookViewSet(ModelViewSet):
    # 遊客只讀,用戶可增刪改查
    permission_classes = [IsAuthenticatedOrReadOnly]
​
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookSerializer

二、特殊路由映射的請求

用主鍵爲2的帳號登陸得到的token,卻可以經過 /api/user/center/1/ 訪問的是主鍵爲1的帳號信息,這是有問題的。如何實現用 /api/user/center/ 接口訪問的是本身的詳情信息:算法

實現用戶中心信息自查,不帶主鍵的get請求,走單查邏輯數據庫

urls.pydjango

# 咱們不走pk直接實現只能單查邏輯就不會出現能夠用主鍵1的token訪問2的帳號
urlpatterns = [
    # ...
    # /user/center/ => 單查,不能走路由組件,只能自定義配置映射關係
    url('^user/center/$', views.UserCenterViewSet.as_view({'get': 'user_center'})),
]

views.py後端

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class UserCenterViewSet(GenericViewSet):
    permission_classes = [IsAuthenticated, ]
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializers.UserCenterSerializer
​
    def user_center(self, request, *args, **kwargs):
        # request.user就是前臺帶token,在通過認證組件解析出來的,
        # 再通過權限組件IsAuthenticated的校驗,因此request.user必定有值,就是當前登陸用戶
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)

三、token刷新機制

drf-jwt直接提供刷新功能,能夠運用在像12306這樣安全性要求較高的網站api

第一個token由登陸簽發,以後的全部正常邏輯,都須要發送兩次請求,第一次刷新token的請求,第二次纔是正常的邏輯請求緩存

settings.py安全

import datetime
​
JWT_AUTH = {
    # 配置過時時間
    'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=5),
​
    # 是否可刷新
    'JWT_ALLOW_REFRESH': True,
    # 刷新過時時間
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

urls.py服務器

from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken
urlpatterns = [
    url('^login/$', ObtainJSONWebToken.as_view()),  # 登陸簽發token接口
    url('^refresh/$', RefreshJSONWebToken.as_view()),  # 刷新toekn接口
]

測試:框架

post請求獲取token,再經過刷新接口發送帶token的數據便可

四、認證組件項目使用:多方式登陸

urls.py

# 自定義登陸(重點):post請求 => 查操做(簽發token返回給前臺) - 自定義路由映射
url('^user/login/$', views.LoginViewSet.as_view({'post': 'login'})),

views.py

# 重點:自定義login,完成多方式登陸
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class LoginViewSet(ViewSet):
    # 登陸接口,要取消全部的認證與權限規則,也就是要作局部禁用操做(空配置)
    authentication_classes = []
    permission_classes = []
​
    # 須要和mixins結合使用,繼承GenericViewSet,不須要則繼承ViewSet
    # 爲何繼承視圖集,不去繼承工具視圖或視圖基類,由於視圖集能夠自定義路由映射:
    #       能夠作到get映射get,get映射list,還能夠作到自定義(靈活)
    def login(self, request, *args, **kwargs):
        serializer = serializers.LoginSerializer(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        token = serializer.context.get('token')
        return Response({"token": token})

serializers.py

from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
​
# 重點:自定義login,完成多方式登陸
class LoginSerializer(serializers.ModelSerializer):
    # 登陸請求,走的是post方法,默認post方法完成的是create入庫校驗,因此惟一約束的字段,會進行數據庫惟一校驗,致使邏輯相悖
    # 須要覆蓋系統字段,自定義校驗規則,就能夠避免完成多餘的沒必要要校驗,如惟一字段校驗
    username = serializers.CharField()
    class Meta:
        model = models.User
        # 結合前臺登陸佈局:採用帳號密碼登陸,或手機密碼登陸,佈局一致,因此無論帳號仍是手機號,都用username字段提交的
        fields = ('username', 'password')
​
    def validate(self, attrs):
        # 在全局鉤子中,才能提供提供的所需數據,總體校驗獲得user
        # 再就能夠調用簽發token算法(drf-jwt框架提供的),將user信息轉換爲token
        # 將token存放到context屬性中,傳給外鍵視圖類使用
        user = self._get_user(attrs)  # 調用自身內部的多方式登陸邏輯
        payload = jwt_payload_handler(user)  # 將user放入產生payload
        token = jwt_encode_handler(payload)  # 將payload放入產生token
        self.context['token'] = token  # 將token返回給視圖函數
        return attrs
​
    # 多方式登陸
    def _get_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        import re
        if re.match(r'^1[3-9][0-9]{9}$', username):
            # 手機登陸
            user = models.User.objects.filter(mobile=username, is_active=True).first()
        elif re.match(r'^.+@.+$', username):
            # 郵箱登陸
            user = models.User.objects.filter(email=username, is_active=True).first()
        else:
            # 帳號登陸
            user = models.User.objects.filter(username=username, is_active=True).first()
        if user and user.check_password(password):
            return user
​
        raise ValidationError({'user': 'user error'})

五、權限組件項目使用:VIP用戶權限

首先在user表中有兩個用戶,而後建立vip組,操做user與group關係表使一個用戶是VIP一個用戶不是

自定義權限校驗規則 permissions.py

from rest_framework.permissions import BasePermission
​
from django.contrib.auth.models import Group
class IsVipUser(BasePermission):
    def has_permission(self, request, view):
        if request.user and request.user.is_authenticated:  # 必須是合法用戶
            try:
                vip_group = Group.objects.get(name='vip')
                if vip_group in request.user.groups.all():  # 用戶可能不屬於任何分組
                    return True  # 必須是vip分組用戶
            except:
                passreturn False

urls.py

router.register('cars', views.CarViewSet, 'car')

views.py

from .permissions import IsVipUser
class CarViewSet(ModelViewSet):
    permission_classes = [IsVipUser]
​
    queryset = models.Car.objects.all()
    serializer_class = serializers.CarSerializer

serializers.py

class CarSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name', )

六、頻率組件

一、頻率校驗規則

一、只有認證、權限校驗都經過了纔會執行,頻率組件的目的是限制接口的返回頻率,判斷是否在規定時間內超過訪問

二、須要配置文件settings中配置訪問的頻率

三、在頻率組件中設置緩存來存儲接口訪問的key,一旦清除緩存就不會進行校驗

四、將頻率組件配置給須要限制的視圖類進行局部配置便可

二、自定義頻率類

自定義頻率類是最多見的:短信接口,一分鐘只能發一次

  1. 繼承SimpleRateThrottle

  2. 設置類實現scope,值就是一個字符串,與settings中的DEFAULT_THROTTLE_RATES進行對應,DEFAULT_THROTTLE_RATES設置scope綁定頻率規則

  3. 重寫get_cache_key(self, request, view) 方法,字段限制條件

    • 不知足限制條件,返回None,表明該請求不須要進行頻率限制

    • 知足限制條件,返回一個字符串,返回一個字符串(動態的),表明該請求須要進行頻率限制 (短信頻率限制類,返回 "throttling_%(mobile)s" % {"mobile": 實際請求來的電話})

系統頻率類:

  1. UserRateThrottle: 限制全部用戶訪問頻率

  2. AnonRateThrottle:只限制匿名用戶訪問頻率

三、頻率組件的使用:請求方式頻率限制

自定義throttles.py

from rest_framework.throttling import SimpleRateThrottle
# 只限制查接口的頻率,不限制增刪改的頻率
class MethodRateThrottle(SimpleRateThrottle):
    scope = 'method'
    def get_cache_key(self, request, view):
        # 只有對get請求進行頻率限制
        if request.method.lower() not in ('get', 'head', 'option'):
            return None
​
        # 區別不一樣的訪問用戶,之間的限制是不衝突的
        if request.user.is_authenticated:
            ident = request.user.pk
        else:
            # get_ident是BaseThrottle提供的方法,會根據請求頭,區別匿名用戶,
            # 保證不一樣客戶端的請求都是表明一個獨立的匿名用戶
            ident = self.get_ident(request)
        return self.cache_format % {'scope': self.scope, 'ident': ident}

settings.py

REST_FRAMEWORK = {
# ...
# 頻率規則配置
'DEFAULT_THROTTLE_RATES': {
# 只能設置 s,m,h,d,且只須要第一個字母匹配就ok,m = min = maaa 就表明分鐘
'user': '3/min', # 配合drf提供的 UserRateThrottle 使用,限制全部用戶訪問頻率
'anon': '3/min', # 配合drf提供的 AnonRateThrottle 使用,只限制匿名用戶訪問頻率
'method': '3/min',
},
}

views.py

from .throttles import MethodRateThrottle
class CarViewSet(ModelViewSet):
    throttle_classes = [MethodRateThrottle]
​
    queryset = models.Car.objects.all()
    serializer_class = serializers.CarSerializer

七、異常組件項目使用:記錄異常信息到平常文件中

若是前端錯誤的請求返回的信息具備格式的,然後端異常返回的信息沒有處理很難看,所以咱們能夠自定義異常組件處理服務器異常信息

自定義exception.py

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
    # 只處理客戶端異常,不處理服務器異常,
    # 若是是客戶端異常,response就是能夠直接返回給前臺的Response對象
    response = drf_exception_handler(exc, context)
​
    if response is None:
        # 沒有處理的服務器異常,處理一下
        # 其實給前臺返回 服務器異常 幾個字就好了
        # 那咱們處理異常模塊的目的是 無論任何錯誤,都有必要進行日誌記錄(線上項目只能經過記錄的日誌查看出現過的錯誤)
        response = Response({'detail': '%s' % exc})
​
    # 須要結合日誌模塊進行日誌記錄的:項目中講
    return response

settings.py

REST_FRAMEWORK = {
    # ...
    # 異常模塊
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',  # 原來的,只處理客戶端異常
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
相關文章
相關標籤/搜索