def 入门-精通 08days

发布时间 2023-12-27 15:00:35作者: 拆尼斯、帕丁顿

认证组件

  使用步骤
    1 登录功能
    2 认证类
        1 写一个类,继承BaseAuthentication
        2 重写 authenticate(self, request) 方法
        3 在方法内部,根据用户携带的 数据【token】,判断是否登录[核心逻辑],如果登录就继续往后走[返回两个变量],如果没登录,抛异常,就不会往后走[视图的方法就不执行了] 
        
    3 在视图类中使用 ---【这个视图类管理的所有方法,都会有登录认证的校验】
    class BookDetailView(GenericViewSet):
        authentication_classes = [LoginAuth]
    4 在配置文件中配置,所有接口都会有,包括登录接口
        REST_FRAMEWORK = {
        # 全局配置登录认证
        'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth']
        }
   5 局部禁用:
      class 视图类(APIView):
            authentication_classes = []
            
            
 #2  authenticate中如何判断是否登录
class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 完成对用户的校验
        # 当次请求request
        token = request.query_params.get('token') or request.META.get('HTTP_TOKEN')
        # 表中校验
        user_token = UserToken.objects.filter(token=token).first()
        # 当前登录用户
        if user_token:
            user = user_token.user
            # 校验过后,返回两个值
            # return后,在后续的视图类的方法中 通过 request.user 就能取到当前登录用户
            # 如果不按这个规则来,后续request.user取不到当前登录用户
            return user, user_token
        else:
            raise AuthenticationFailed("您没有登录")

权限组件

访问某个接口有没有权限---》大部分情况下需要登录后再校验
# 使用方式跟认证雷同
   1 写权限类
        1 写一个类,继承BasePermission
        2 重写 has_permission 方法
        3 在方法内部,根据用户的 类型,判断有没有权限,如果有返回True,如果没有返回False
            显示中文提示:self.message=中文
        
    3 在视图类中使用 ---【这个视图类管理的所有方法,都会有权限控制】
    class BookDetailView(GenericViewSet):
        permission_classes = [CommonPermission]
    4 在配置文件中配置,所有接口都会有,包括登录接口
        REST_FRAMEWORK = {
        # 全局配置登录认证
        'DEFAULT_PERMISSION_CLASSES': ['']
        }
   5 局部禁用:
      class 视图类(APIView):
            permission_classes = []
            
            
# 权限类代码
from rest_framework.permissions import BasePermission


# 只有超级管理员能访问,其他人都不能访问
class CommonPermission(BasePermission):
    def has_permission(self, request, view):
        # 到这,正常应该登录完了,登录完了 request.user 当前登录用户
        # 拿到用户后判断用户是否有权限--》用户表汇总用户类型字段 user_type--》根据类型判断权限
        try:
            user_type = request.user.user_type
            if user_type == 2:
                return True
            else:
                # user.get_字段名_display()  返回这个字段的choice对应的文字
                self.message = '您是:%s,您没有权限操作这个' % request.user.get_user_type_display()
                return False
        except Exception as e:
            # 未登录用户也没有权限
            self.message = '您没有登录,您没有权限操作这个'
            return False

# 补充,后续的权限类,可能逻辑更复杂
    # 后续可能使用ACL权限控制,RBAC权限控制
    # 开直播 in [看视频]
    #开直播 in  [看视频,收礼物,开直播]

 

权限代码更改

from rest_framework.permissions import BasePermission


# 只有超级管理员能访问,其他人都不能访问
class CommonPermission(BasePermission):
    def has_permission(self, request, view):
        # 到这,正常应该登录完了,登录完了 request.user 当前登录用户
        # 拿到用户后判断用户是否有权限--》用户表汇总用户类型字段 user_type--》根据类型判断权限
        try:
            # 后续可能使用ACL权限控制,RBAC权限控制
            # 开直播 in [看视频]
            # 开直播 in  [看视频,收礼物,开直播]
            user_type = request.user.user_type
            if user_type == 2:
                return True
            else:
                # user.get_字段名_display()  返回这个字段的choice对应的文字
                self.message = '您是:%s,您没有权限操作这个' % request.user.get_user_type_display()
                return False
        except Exception as e:
            # 未登录用户也没有权限
            if 'login' in request.path:
                return True
            else:
                self.message = '您没有登录,您没有权限操作这个'
                return False

频率组件

控制用户访问某个接口的频次
    比如一分钟只能访问5次
    
    
# 使用步骤---跟上面有区别
    1 写个类,继承 SimpleRateThrottle
    2 重写方法:def get_cache_key(self, request, view): 返回什么就以什么做限制
    3 写一个类属性:
        rate = '5/minute'   # second  minute hour day
        rate = '5/m'
        rate = '5/d'
        
   4 视图类上配置
    class BookView(ViewSetMixin, ListCreateAPIView):
        throttle_classes = [CommonThrottle]
   5 全局配置--全局所有接口都会有频率限制
    REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
    }
   6 局部禁用
    class BookView(ViewSetMixin, ListCreateAPIView):
        throttle_classes = []
        
        
# 频率类代码
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class CommonThrottle(SimpleRateThrottle):
    rate = '5/d'
    def get_cache_key(self, request, view):
        ip = request.META.get('REMOTE_ADDR')
        return ip  # 返回什么,就以什么做频率限制(ip,用户id)

权限组件源码分析

1 为什么写一个类继承BasePermission,重写has_permission
  -可以不继承这个类,只重写这个方法也可以
2 权限类中 self.message 会返回给前端
3 局部配置:放在视图类中:permission_classes = [CommonPermission]
4 全局配置,配置在配置文件中也可以--》视图类中就没有
  就会使用:permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
  优先从你项目的配置文件配置的DEFAULT_PERMISSION_CLASSES取,如果没有就会去drf的配置文件DEFAULT_PERMISSION_CLASSES取
# 1 从哪里开始找?
  -继承APIView后,权限是在执行视图类的方法之前执行的
  -执行视图类的方法---》dispath中
def dispatch(self, request, *args, **kwargs):

try:

  # 三大认证:认证,权限,频率
            self.initial(request, *args, **kwargs)
            ####执行视图类的方法开始###
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
            ####执行视图类的方法结束###
        except Exception as exc:
            response = self.handle_exception(exc)
        return self.response
    
#2 找到APIView的initial
    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)#认证
        self.check_permissions(request)#权限 
        self.check_throttles(request)#频率
        
# 3 self.check_permissions(request)---APIView
    def check_permissions(self, request):
        # self是 视图类的对象
        #self.get_permissions [CommonPermission(),]--->咱们配置的一个个权限类的对象,放到列表中
        # permission 权限类的对象
        for permission in self.get_permissions():
            # 咱们写的权限类,要重写has_permission,传了俩参数
            # 参数:request 是新的request
            # 参数:self 是?视图类的对象,就是咱么在视图类中写的self,它里面有
            # request和action等
            if not permission.has_permission(request, self):
                # 视图类的对象,没有权限
                self.permission_denied(
                    request,
                    # 从权限类的对象中反射了message,就是写的给前端看的文字
                    message=getattr(permission, 'message', None),
                    #响应状态码
                    code=getattr(permission, 'code', None)
                )
                
# 4 self.get_permissions() ---> APIView的
    def get_permissions(self):
        # 咱么视图类上配置的 permission_classes = [CommonPermission]
        # 返回值是 [CommonPermission(),]--->咱们配置的一个个权限类的对象,放到列表中
        return [permission() for permission in self.permission_classes]
    
    
# 5 self.permission_denied  --》APIView中
  def permission_denied(self, request, message=None, code=None):
        if request.authenticators and not request.successful_authenticator:
            raise exceptions.NotAuthenticated()
        # 抛了异常,会被全局异常捕获,detai会放在响应体中,code会放在响应状态码中
        raise exceptions.PermissionDenied(detail=message, code=code)
        

认证类源码

-继承APIView后,权限是在执行视图类的方法之前执行的
    -执行视图类的方法---》dispath中
        def dispatch(self, request, *args, **kwargs):

        try:
            # 三大认证:认证,权限,频率
            self.initial(request, *args, **kwargs)
            ####执行视图类的方法开始###
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
            ####执行视图类的方法结束###
        except Exception as exc:
            response = self.handle_exception(exc)
        return self.response
    
#2 找到APIView的initial
    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)#认证
        self.check_permissions(request)#权限 
        self.check_throttles(request)#频率
        
# 3 self.perform_authentication(request)
  def perform_authentication(self, request):
        request.user # 新的request对象
        
        
# 4 Request类中找 user 方法包装成了数据属性
    from rest_framework.request import Request
    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                # self是 新的request对象
                self._authenticate() # 一开始没有,就走了self._authenticate()
        return self._user
    
    
# 5  Request类中找_authenticate()
    def _authenticate(self):
        # 拿出你配置在视图类上一个个认证类的对象  LoginAuth()
        # authenticator就是LoginAuth()
        for authenticator in self.authenticators:
            try:
                # 为什么咱么要重写 authenticate
                #self是谁?request就是认证类的 def authenticate(self, request):
                # 正常返回两个值:return user, user_token
                # 如果抛了异常:AuthenticationFailed--》捕获了APIException
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise
            if user_auth_tuple is not None:#正常返回了两个值
                self._authenticator = authenticator
                # 解压赋值:self是request
                #后续在视图类中 request.user 就是当前登录用户
                self.user, self.auth = user_auth_tuple
                return
        self._not_authenticated()
        
        
# 6 咱们视图类中得认证类可以配置多个
    -如果 第一个认证类,就返回了两个值
    -后续的认证类就不走了
    -如果认证类中要返回两个值,视图类中配了多个---》把返回两个值的放在最后
    -返回的两个值,第一个给了request.user,第一个给了request.auth,后续视图类中可以取出来
    
    
# 7 self.authenticators ---》 Request中
# Request类实例化得到对象,传入authenticators 最终给了
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
     self.authenticators = authenticators or ()
    
    
# 8 哪里对Request类实例化了? APIView中
APIView 中dispatch中---》包装了新的request
request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
        
        
# 9 self.get_authenticators()  在APIView中 self是视图类的对象
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]
    
    
# 总结:    
1 写一个类,重写authenticate
2 校验失败抛异常--》捕获
3 通过返回两个值  当前登录用户,token
    不返回两个值--》后续的request.user 不是当前登录用户
4 视图类上局部配置和配置文件全局配置跟 权限一模一样