DRF之认证组件

一:认证组件介绍

(1)认证组件入口

(1)视图

APIView的dispath(self, request, *args, **kwargs)   

(2)任务分发

dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证

(3)认证组件

   self.perform_authentication(request)

(4)认证功能介绍

  (1)其主要用来对用户进行认证

  (2)检验用户 ----> 普通游客 | 合法用户 | 非法用户

  (3)游客:代表认证通过 直接进入下一步(权限校验)

  (2)合法用户:携带验证信息进行验证 验证通过将用户存储进入request.user中 再次进行下一步校验

  (3)非法用户:携带验证信息进行验证 验证失败直接抛出异常 返回403权限问题

(2)源码分析

(1)第一步APIView/dispatch/initial

 def initial(self, request, *args, **kwargs):# Ensure that the incoming request is permitted
        self.perform_authentication(request) # 认证功能
        self.check_permissions(request)      # 访问权限功能
        self.check_throttles(request)       # 频率功能

(2)第二步进入认证功能

   def perform_authentication(self, request):

        request.user   # 得到一个方法属性 get方法
     PS: 如果是复制属性 应该是request.user = XXX

(3)第三步进入request/user

    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()     # 没有用户认证处用户
        return self._user  # 如果有直接返回用户

(4)第四步_authenticate

    def _authenticate(self):
        # self.authenticators:一堆认证类产生序列化认证对象的集合体(列表)
        for authenticator in self.authenticators:   # 循环遍历集合体拿到一个个认证对象
            try:
                '''
                authenticator.authenticate:认证对象调用认证功能
                user_auth_tuple:拿到登录的用户与用户信息的返回值
                
                调用改方法传入两个参数 self,request
                self:当前认证类对象
                request:当前请求用户
                
                '''
                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.author
                self.user, self.auth = user_auth_tuple

                return
        # 如果返回值为空 代表认证通过 但是没有登录用户 与验证信息 表示当前用户为游客用户
        self._not_authenticated()

(5)自定义认证组件

(1)全局settings文件配置

REST_FRAMEWORK = {
# 认证类配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'api.authentications.MyAuthentication',  # 自定义认证组件
    ],
   
}

(2)全局settings默认配置

 # 认证类配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
         'rest_framework.authentication.SessionAuthentication',
         'rest_framework.authentication.BasicAuthentication',
       
    ],

(3)自定义文件配置api/Authentication

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from . import models
class MyAuthentication(BaseAuthentication):
    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('认证信息有误 非法用户')  # 非法的请求 直接抛出异常

        # 解析合法的用户
        if not auth_list[1] == 'qwe.asd.zxc':   # 假设通过解析'qwe.asd.zxc' 可以解析处合法用户
            raise AuthenticationFailed('用户校验失败 非法用户')

        user_obj = models.User.objects.filter(username='admin').first()
        if not user_obj:
            raise AuthenticationFailed('用户数据有误 非法用户')
        return (user_obj,None)   # 合法将用户返回 验证信息不返回

(4)视图层

from rest_framework.views import APIView
from rest_framework.response import Response

class TestAPIView(APIView):
    def get(self,request,*args,**kwargs):
        '''
        测试一:前段不输入校验信息 获取用户属于匿名用户
        测试二:前段加入验证信息  获取用户属于解析用户 admin
        '''
        print(request.user)
        return Response(
            {
                'status':0,
                'msg':'测试成功'
            }
        )

 二:权限组件介绍

(1)权限组件入口

(1)视图

APIView的dispath(self, request, *args, **kwargs)   

(2)任务分发

dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证

(3)权限组件

self.check_permissions(request)   

(2)源码分析

(1)源码分析

 def check_permissions(self, request):
        '''
        get_permissions:产生权限对象的类
        参数:
            self:产生权限的类的对象
            request:用户请求
            self:views实例化产生对象
        '''
        for permission in self.get_permissions():  # 循环遍历
            '''
            has_permission:返回值布尔值
            
            '''
            if not permission.has_permission(request, self):  # 判断布尔值 如果为真则获取所有权限 反之则为假
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
      # PS:默认登录用户 与游客有全部权限

(2)系统权限类

(1)AllowAny:游客与登录用户都含有权限

class AllowAny(BasePermission):

    def has_permission(self, request, view):
        return True

(2)IsAuthenticated:登录用户且验证通过的用户

class IsAuthenticated(BasePermission):
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

(3)IsAdminUser:登录用户 且必须是后台管理员

class IsAdminUser(BasePermission):
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_staff)

(4)IsAuthenticatedOrReadOnly:只读请求 或者合法用户

  (1)游客:只读

  (2)登录用户:含有全部权限

class IsAuthenticatedOrReadOnly(BasePermission):

    def has_permission(self, request, view):
        return bool(
            request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated
        )

(3)代码示例

(1)全局settings

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

(2)视图层

from rest_framework.permissions import IsAuthenticated


# 只有登录用户才有权限
class TestAuthenticatedAPIView(APIView):
    permission_classes = [IsAuthenticated]  # 使用 IsAuthenticated 不使用全局AllowAny

    def get(self, request, *args, **kwargs):
        return Response(
            {
                'status': 0,
                'msg': "登录且合法用户可以访问"
            }
        )


# 游客只读,登录无限制
from rest_framework.permissions import IsAuthenticatedOrReadOnly


class TestAuthenticatedOrReadOnlyAPIView(APIView):
    permission_classes = [IsAuthenticatedOrReadOnly]

    def get(self, request, *args, **kwargs):
        return Response({
            'status': 0,
            'msg': '游客只读 登录用户无限制'
        })

    def post(self, request, *args, **kwargs):
        return Response({
            'status': 0,
            'msg': '游客只读 登录用户无限制'
        })

(4)自定义权限

(1)api/permissions


from
django.contrib.auth.models import Group from rest_framework.permissions import BasePermission from rest_framework.exceptions import AuthenticationFailed class MyPermission(BasePermission): def has_permission(self, request, view): print(self) # <api.permissions.MyPermission object at 0x0000000005673C50> 当前权限对象 print(request) # <api.permissions.MyPermission object at 0x0000000005673C50> 请求对象 print(view) # <api.views.TestAdminOrReadOnlyAPIView object at 0x0000000005673518> view对象 # 只读接口 read_only = request.method in ('GET', 'HEAD', 'OPTIONS') # group为有权限的分组 group = Group.objects.filter(name='管理员').first() # groups为当前用户所属的所有分组 groups = request.user.groups.all() data1 = group and groups # group groups 必须有值 data2 = group in groups # 有权限分组 必须在所有分组中 # 读接口大家都有权限,写接口必须为指定分组下的登陆用户 return read_only or (data1 and data2)

(2)视图层

from .permissions import MyPermission


class TestAdminOrReadOnlyAPIView(APIView):
    permission_classes = [MyPermission]

    def get(self, request, *args, **kwargs):
        return Response(
            {
                'status':0,
                'msg':"游客可以读取数据 但是不能写数据"
            }
        )

    def post(self, request, *args, **kwargs):
        return Response(
            {
                "status":0,
                'msg':'管理员可以读写'
            }
        )

 三:频率组件

(1)权限组件入口

(1)视图

APIView的dispath(self, request, *args, **kwargs)   

(2)任务分发

dispath方法内 self.initial(request, *args, **kwargs) 进入三大认证

(3)权限组件

self.check_throttles(request)   # 频率组件

(2)核心源码分析

 for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            # 只要频率限制了,allow_request 返回False了,才会调用wait
            throttle_durations.append(throttle.wait())

            if throttle_durations:
                # Filter out `None` values which may happen in case of config / rate
                # changes, see #1438
                durations = [
                    duration for duration in throttle_durations
                    if duration is not None
                ]

                duration = max(durations, default=None)
                self.throttled(request, duration)

PS:

  (1)遍历配置的频率认证类,初始化得到一个个频率认证类对象(会调用频率认证类的 __init__() 方法)
  (2)频率认证类对象调用 allow_request 方法,判断是否限次(没有限次可访问,限次不可访问)
  (3)频率认证类对象在限次后,调用 wait 方法,获取还需等待多长时间可以进行下一次访问
        注:频率认证类都是继承 SimpleRateThrottle 类

 (3)自定义频率类

(1)全局settings文件配置

  # 频率限制条件配置
    'DEFAULT_THROTTLE_RATES': {
        'send_msg': '1/min'
    },

(2)api/throttles.py

from rest_framework.throttling import SimpleRateThrottle

class SendMsgRateThrottle(SimpleRateThrottle):
    scope = 'send_msg'  # 自定义一个字符串

    def get_cache_key(self, request, view):
        phone = request.query_params.get('phone')   # 获取前段get拼接值

        if not phone:  # 如果 没有值 说明无任何访问限制
            return None  # 按照源码格式来 固定返回None
          
        # 按照源码格式 此处以手机号作为身份标识
        return 'throttle_%(scope)s_%(ident)s' % {'scope': self.scope, 'ident': phone}   

(3)路由层

    url(r'^send_msg/$', views.TestThrottles.as_view()),

(4)视图层

from .throttles import SendMsgRateThrottle   # 导入自定义的频率类

class TestThrottles(APIView):
    throttle_classes = [SendMsgRateThrottle]  # 局部配置

    def get(self, request, *args, **kwargs):
        return Response({
            'status': 0,
            'msg': '访问频率get方式测试成功'
        })

    def post(self, request, *args, **kwargs):
        return Response({
            'status': 0,
            'msg': '访问频率post方式测试成功'
        })

PS:

  (1)对api/send_msg/?phone = xxx 有限制

# 例如
http://127.0.0.1:8000/api/send_msg/?phone=1234654646

  (2)对 /api/send_msg/ 或其他接口发送无限制
  (3)对数据包提交phone的/api/phone/接口无限制
  (4)对不是phone(如mobile)字段提交的电话接口无限制

 

posted @ 2019-10-21 20:30  SR丶  阅读(182)  评论(0编辑  收藏  举报