drf三大認證

源碼分析數據庫

  dispath方法內 self.initial(request, *args, **kwargs) 進入三大認證django

        # 認證組件:校驗用戶(遊客,合法用戶,非法用戶)
        # 遊客:表明校驗經過直接進入下一步校驗,
        # 合法用戶:表明校驗經過,將用戶存儲在request.user中,在進入下一步校驗
        # 非法用戶:表明校驗失敗,拋出異常,返回403權限異常結果
        self.perform_authentication(request)
        # 權限組件:校驗用戶權限,必須登陸,全部用戶登陸讀寫,遊客只讀,自定義用戶角色
        # 認證經過:能夠進入下一步校驗
        # 認證失敗:拋出異常,返回403權限異常結果
        self.check_permissions(request)
        # 頻率組件:限制視圖接口被訪問的頻率次數  限制的條件
        # 沒有達到限次:正常訪問接口
        # 達到限次:限制時間內不能訪問,限制時間到達後,能夠從新訪問
        self.check_throttles(request)    

認證組件api

def _authenticate(self):
        # 遍歷拿到一個個認證器,進行認證
        # self.authenticators配置的一堆認證類產生的認證類對象組成的 list
        for authenticator in self.authenticators:
            try:
                # 認證器(對象)調用認證方法authenticate(認證類對象self, request請求對象)
                # 返回值:登錄的用戶與認證的信息組成的 tuple
                # 該方法被try包裹,表明該方法會拋異常,拋異常就表明認證失敗
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            # 返回值的處理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如何有返回值,就將 登錄用戶 與 登錄認證 分別保存到 request.user、request.auth
                self.user, self.auth = user_auth_tuple
                return
        # 若是返回值user_auth_tuple爲空,表明認證經過,可是沒有 登錄用戶 與 登錄認證信息,表明遊客
        self._not_authenticated()

權限組件緩存

self.check_permissions(request)
    認證細則:
    def check_permissions(self, request):
        # 遍歷權限對象列表獲得一個個權限對象(權限器),進行權限認證
        for permission in self.get_permissions():
            # 權限類必定有一個has_permission權限方法,用來作權限認證的
            # 參數:權限對象self、請求對象request、視圖類對象
            # 返回值:有權限返回True,無權限返回False
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

權限六表分析ide

models.py源碼分析

from django.db import models

# Create your models here.

# 若是自定義User表後,再另外一個項目中採用原生User表,完成數據遷移時,可能失敗
# 1.卸載Django,從新裝
# 2.將Django.contrib下面的admin\auth下的數據庫記錄文件清空
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    mobile = models.CharField(max_length=11,unique=True)

    class Meta:
        db_table = 'api_user'
        verbose_name = '用戶表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

t_model.pypost

# django腳本話啓動
import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day74.settings")
django.setup()

from api import models

user = models.User.objects.first()
# print(user.username)
# print(user.groups.first().name)
# print(user.user_permissions.first().name)

from django.contrib.auth.models import Group
group = Group.objects.first()
# print(group.name)
# print(group.user_set.first().username)
# print(group.permissions.first().name)


from django.contrib.auth.models import Permission
p_16 = Permission.objects.filter(pk=16).first()
print(p_16.user_set.first().username)
p_17 = Permission.objects.filter(pk=17).first()
print(p_17.group_set.first().name)

自定義認證類url

settings.pyspa

# drf配置
REST_FRAMEWORK = {
    # 認證類配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 'rest_framework.authentication.SessionAuthentication',
        # 'rest_framework.authentication.BasicAuthentication',
        'api.authentications.MyAuthentication',
        ],
}

authentications.pyrest

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from . import models
class MyAuthentication(BaseAuthentication):
    # 同前臺請求拿認證信息auth,沒有auth是遊客,返回None
    # 有auth進行校驗   失敗是非法用戶拋異常,成功是合法用戶,返回(用戶,認證信息)
    def authenticate(self, request):
        # 前臺在請求頭攜帶信息。默認用Authorization字段攜帶認證信息
        # 後臺固定在請求對象的META字段中HTTP_AUTHORIZATION獲取
        auth = request.META.get('HTTP_AUTHORIZATION',None)

        # 處理遊客
        if auth is None:
            return None

        # 設置認證字段
        auth_list = auth.split()
        # 校驗合法仍是非法
        if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'):
            raise AuthenticationFailed('認證信息有誤,非法用戶')

        # 合法用戶須要從auth_list[1]中解析出來
        if auth_list[1] != 'abc.123.xyz': # 校驗失敗
            raise AuthenticationFailed('用戶校驗失敗,非法用戶')

        user = models.User.objects.filter(username='admin').first()

        if not user:
            raise AuthenticationFailed('用戶數據有誤,非法用戶')
        return (user, None)

系統權限類

1)AllowAny:
    認證規則所有返還True:return True
        遊客與登錄用戶都有全部權限

2) IsAuthenticated:
    認證規則必須有登錄的合法用戶:return bool(request.user and request.user.is_authenticated)
        遊客沒有任何權限,登錄用戶纔有權限
    
3) IsAdminUser:
    認證規則必須是後臺管理用戶:return bool(request.user and request.user.is_staff)
        遊客沒有任何權限,登錄用戶纔有權限

4) IsAuthenticatedOrReadOnly
    認證規則必須是隻讀請求或是合法用戶:
        return bool(
            request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated
        )
        遊客只讀,合法用戶無限制

  urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^test', views.TestAPIView.as_view()),
   url(r'^test1', views.TestAuthenticatedAPIView.as_view()),
]

settings.py

REST_FRAMEWORK = {
    # 權限類配置
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
}

api/views.py

from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet,ViewSet
from utils.response import APIResponse
from rest_framework.permissions import IsAuthenticated

class TestAPIView(APIView):
    def get(self, request, *args, **kwargs):
        return APIResponse(0, 'test get ok')


class TestAuthenticatedAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request, *args, **kwargs):
        return APIResponse(0,'test 登陸才能訪問接口 ok')

自定義權限類

  建立繼承BasePermission的權限類,  實現has_permission方法,  實現體根據權限規則 肯定有無權限,  進行全局或局部配置

認證規則:  知足設置的用戶條件,表明有權限,返回True,  不知足設置的用戶條件,表明有權限,返回False

urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^test2', views.TestAdminOrReadOnlyAPIView.as_view()),
]

permissions.py

from rest_framework.permissions import BasePermission
from django.contrib.auth.models import Group
class MyPermission(BasePermission):
  # 只讀接口判斷 def has_permission(self, request, view): r1
= request.method in ('GET', 'HEAD', 'OPTIONS')
    # group爲當前所屬的全部分組 group
= Group.objects.filter(name='管理員').first() groups = request.user.groups.all() r2 = group and groups r3 = group in groups
    # 讀接口都權限,寫接口必須指定分組下的登陸用戶
return r1 or (r2 and r3)

views.py

# 遊客只讀,登陸用戶只讀,只有登陸用戶屬於管理員分組,才能夠增刪改
from
utils.permissions import MyPermission class TestAdminOrReadOnlyAPIView(APIView): permission_classes = [MyPermission]
  # 全部用戶均可以訪問 def
get(self, request, *args, **kwargs): return APIResponse(0, '自定義讀 OK')
  # 必須是自定義管理員分組下的用戶 def post(self, request,
*args, **kwargs): return APIResponse(0, '自定義寫 OK')

 自定義頻率類

  自定義一個繼承 SimpleRateThrottle 類 的頻率類,設置一個 scope 類屬性,屬性值爲任意見名知意的字符串,在settings配置文件中,配置drf的DEFAULT_THROTTLE_RATES,格式爲 {scope字符串: '次數/時間'},在自定義頻率類中重寫 get_cache_key 方法

settings.py

REST_FRAMEWORK = {
     # 頻率限制條件配置
    'DEFAULT_THROTTLE_RATES': {
        'sms':'3/min'
    },
}

api/url.py

urlpatterns = [
    url(r'^sms/$', views.TestSMSAPIView.as_view()),
]

api/throttles.py

from rest_framework.throttling import SimpleRateThrottle
class SMSRateThrottle(SimpleRateThrottle):
    scope = 'sms'
    # 只對提交手機號的get方法進行限制
    def get_cache_key(self, request, view):
        mobile = request.query_params.get('mobile')
        # 沒有手機,不作頻率限制
        if not mobile:
            return None
        # 返回能夠根據手機號變化,且不易重複的字符串,做爲操做緩存的key
        return 'throttle_%(scope)s_%(ident)s' % {'scope': self.scope, 'ident': mobile}

views.py

class TestSMSAPIView(APIView):
    # 局部配置頻率校驗
    throttle_classes = [SMSRateThrottle]
    def get(self, request, *args, **kwargs):
        return APIResponse(0,'get 獲取驗證碼 OK')
    def post(self, request, *args, **kwargs):
        return APIResponse(0,'post 獲取驗證碼 OK')
相關文章
相關標籤/搜索