DRF框架基礎八之三大認證組件和異常處理組件


系統權限類使用

圖書接口:游客只讀,用戶可增刪改查權限使用

from rest_framework.permissions import IsAuthenticatedOrReadOnly
class BookViewSet(ModelViewSet):
   # 游客只讀,用戶可增刪改查
   permission_classes = [IsAuthenticatedOrReadOnly]

   queryset = models.Book.objects.all()
   serializer_class = serializers.BookSerializer

 

 

特殊路由映射的請求

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

urls.py
# 用路由組件配置,形成的映射關系是 /user/center/ => list | user/center/(pk)/ => retrieve
# router.register('user/center', views.UserCenterViewSet, 'center')

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直接提供刷新功能

"""
1)運用在像12306這樣極少數安全性要求高的網站
2)第一個token由登錄簽發
3)之后的所有正常邏輯,都需要發送兩次請求,第一次是刷新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接口
]
Postman
# 接口:/api/refresh/
# 方法: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)
       token = jwt_encode_handler(payload)
       self.context['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用戶權限

數據准備
"""
1)User表創建兩條數據
2)Group表創建一條數據,name叫vip
3)操作User和Group的關系表,讓1號用戶屬於1號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:
               pass

       return False
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', )
urls.py
router.register('cars', views.CarViewSet, 'car')

 

 

頻率組件

重點
"""
1)如何自定義頻率類
2)頻率校驗的規則
3)自定義頻率類是最常見的:短信接口一分鍾只能發生一條短信
"""
自定義頻率類
"""
1)自定義類繼承SimpleRateThrottle
2)設置類實現scope,值就是一個字符串,與settings中的DEFAULT_THROTTLE_RATES進行對應
DEFAULT_THROTTLE_RATES就是設置scope綁定的類的頻率規則:1/min 就代表一分鍾只能訪問一次
3)重寫 get_cache_key(self, request, view) 方法,指定限制條件
不滿足限制條件,返回None:代表對這類請求不進行頻率限制
滿足限制條件,返回一個字符串(是動態的):代表對這類請求進行頻率限制,比如用戶可以返回用戶的id,因為需要識別是誰發來的,具有針對的限制
短信頻率限制類,返回 "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 .permissions import IsVipUser
from .throttles import MethodRateThrottle
class CarViewSet(ModelViewSet):
   permission_classes = [IsVipUser]
   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',
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM