DRF框架(六)——三大認證組件之認證組件、權限組件


drf認證組件

用戶信息表
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    mobile = models.CharField(max_length=11,unique=True)

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

    def __str__(self):
        return self.username

源碼分析

1.從APIView的dispath(self, request, *args, **kwargs)下手
2.dispath方法內 self.initial(request, *args, **kwargs) 進入三大認證
    認證組件:校驗用戶 —游客、合法用戶、非法用戶
    # 游客:代表校驗通過,直接進入下一步校驗(權限校驗)
    # 合法用戶:代表校驗通過,將用戶存儲在request.user中,再進入下一步校驗(權限校驗)
    # 非法用戶:代表校驗失敗,拋出異常,返回403權限異常結果
    self.perform_authentication(request)  #認證組件
    
      權限組件:校驗用戶權限 - 必須登錄、所有用戶、登錄之后讀寫,游客只讀、自定義用戶角色
    # 認證通過:可以進入下一步校驗(頻率認證)
    # 認證失敗:拋出異常,返回403權限異常結果
    self.check_permissions(request)   #權限組件
    
     頻率組件:限制視圖接口被訪問的頻率次數 - 限制的條件(IP、id、唯一鍵)、頻率周期時間(s、m、h)、頻率的次數(3/s)
    # 沒有達到限次:正常訪問接口
    # 達到限次:限制時間內不能訪問,限制時間達到后,可以重新訪問
    self.check_throttles(request)   #頻率組件

分兩方面:a,b

(a):在initial方法上面——這個是源碼settings文件配置認證組件的地方

 def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    def get_authenticators(self):
       
        return [auth() for auth in self.authentication_classes]  

源碼settings.py文件中APISettings類

DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',  #session認證 'rest_framework.authentication.BasicAuthentication'   #基礎認證
    ]

(b):  self.inital(self,request,*args,**kwargs)    包含認證,權限,頻率三大組件

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)  #認證
        self.check_permissions(request)  #權限
        self.check_throttles(request)  #頻率

(b---1):   self.perform_authentication(request)     認證

    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user

(b---2):Request類的  方法屬性   user    的get方法   

@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate() return self._user

(b---3):self._authenticate()

認證的細則:
    # 做認證
    def _authenticate(self):  #這里的self就是request # 遍歷拿到一個個認證器,進行認證
        # self.authenticators配置的一堆認證類產生的認證類對象組成的 list
     # 即:[auth() for auth in self.authentication_classes] 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()

自定義認證類

從源碼的settings文件可以看出,認證類需要繼承BasicAuthentication(在authentication.py文件)

DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',  #會重新開啟CSRF認證 'rest_framework.authentication.BasicAuthentication'
    ]
1) 創建繼承BaseAuthentication的認證類
2) 重寫authenticate方法
3) 實現體根據認證規則 確定游客、非法用戶、合法用戶 (根據自己的認證規則) 4) 進行全局或局部配置

認證規則
i.沒有認證信息返回None(游客)
ii.有認證信息認證失敗拋異常(非法用戶)
iii.有認證信息認證成功返回用戶與認證信息元組(合法用戶)

utils.authentications.py

# 1)繼承BaseAuthentication類
# 2)重寫authenticate(self, request)方法,自定義認證規則
# 3)自定義認證規則基於的條件:
#       沒有認證信息返回None(游客)
#       有認證信息認證失敗拋異常(非法用戶)
#       有認證信息認證成功返回用戶與認證信息元組(合法用戶)
# 4)完全視圖類的全局(settings文件中)或局部(確切的視圖類)配置

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed   #異常接收
from app01 import models

#繼承BaseAuthentication
class MyAuthentication(BaseAuthentication):
    """
        同前台請求頭拿認證信息auth(獲取認證的字段要與前台約定)
        沒有auth是游客,返回None
        有auth進行校驗
            失敗是非法用戶,拋出異常
            成功是合法用戶,返回 (用戶, 認證信息)
    """
    def authenticate(self, request):   #重寫authenticate方法
    #前台在請求頭攜帶認證信息,
    #且默認規范用 Authorization 字段攜帶認證信息,
    #后台固定在請求對象的META字段中 HTTP_AUTHORIZATION 獲取
        #認證信息auth
        auth = request.META.get('HTTP_AUTHORIZATION',None)#處理游客
        if auth is None:
            return None
        #設置認證字段小規則(兩段式):"auth 認證字符串" 在BasicAuthentication類中有規則范式
        auth_list = auth.split()     #校驗是否還是非法用戶,不是兩段,第一段不是auth就是非法用戶
        if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'):
            raise AuthenticationFailed('認證信息有誤,非法用戶')  #拋異常

        #校驗認證信息第二段從auth_list[1]中解析出來
        # 注:假設一種情況,信息為abc.123.xyz,就可以解析出admin用戶;實際開發,該邏輯一定是校驗用戶的正常邏輯
        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)

在settings文件中配置自定義認證組件

REST_FRAMEWORK = {
    # 認證類配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'utils.authentications.MyAuthentication',
    ],
}

視圖層:views.py

class TestAPIView(APIView):
    def get(self, request, *args, **kwargs):
        # 如果通過了認證組件,request.user就一定有值
        # 游客:AnonymousUser
        # 用戶:User表中的具體用戶對象
        print(request.user)
        return APIResponse(0, 'test get ok')

路由層:urls.py

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

注意使用Postman請求:

使用Postman的get請求,在自定義認證組件獲取用戶,在views視圖通過request.user能打印出來

注意配置:

認證組件一般都是自定義的,不會使用原始的,自定義好需要在settings配置就是全局使用

 

drf權限組件

源碼分析

1.   從APIView的dispatch開始

2.   dispatch 方法內   self.initial(request,*args,**kwargs)

3.   self.initial(request,*args,**kwargs)

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        self.perform_authentication(request)
        self.check_permissions(request) #權限組件
        self.check_throttles(request) 

4. 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)
                )

5.self.get_permissions()  獲取權限類對象列表

    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        return [permission() for permission in self.permission_classes]

6.self.permission_classes 獲取權限settings源碼配置

DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',  #所有權限
    ]

 

在源碼permissions.py中的系統自帶權限類,這里舉例四個

1AllowAny默認是這個權限
    認證規則全部返還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 ) 游客只讀,合法用戶無限制
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')

權限組使用:全局使用,局部使用

全局使用:默認全局配置的權限類是AllowAny,在settings文件中配置

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

局部使用:views.py

#只有登錄后才能訪問
from
rest_framework.permissions import IsAuthenticated class TestAuthenticatedAPIView(APIView): permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): return APIResponse(0, 'test 登錄才能訪問的接口 ok')

自定義權限類

1) 創建繼承BasePermission的權限類
2) 實現has_permission方法
3) 實現體根據權限規則 確定有無權限
4) 進行全局或局部配置

認證規則
i.滿足設置的用戶條件,代表有權限,返回True
ii.不滿足設置的用戶條件,代表有權限,返回False

utils.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為當前用戶所屬的所有分組
        groups = request.user.groups.all()
        r2 = group and groups
        r3 = group in groups
        # 讀接口大家都有權限,寫接口必須為指定分組下的登陸用戶
        return r1 or (r2 and r3)

視圖層:views.py     局部使用自定義的Mypermission來做權限認證

# 游客只讀,登錄用戶只讀,只有登錄用戶屬於 管理員 分組,才可以增刪改
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')

路由層:urls.py

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

Postman使用:

get請求:加不加Authorization,都可以讀

post請求:不加Authorization,就不能進行寫操作,必須要寫Authorization

 


免責聲明!

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



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