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,get请求,登录时候需要在Headers设置 Authorization,然后请求认证

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中,系统权限类,这里举例四个

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 ) 游客只读,合法用户无限制
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     自定义TestAdminOrReadOnlyAPIView

# 游客只读,登录用户只读,只有登录用户属于 管理员 分组,才可以增删改
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,就不能写,必须要写

猜你喜欢

转载自www.cnblogs.com/wangcuican/p/11716871.html