DRF 5 认证权限频率含源码详解

API视图类提供了认证,权限,频率限制的功能,使用方法很简单,在框架内对应的地方写业务逻辑即可.

5.1 认证

简单来说就是:认证就是确定你是谁,认证的方法有很多,

基于密码认证:每次登陆都要输入账号密码,对用户不友好

基于session原理认证:

第一次登陆后服务器给你一个随机字符串,你的浏览器保存下来(这叫cookie),我服务器也保存一份同时还包含于用户的对应关系(这叫session),下次再来浏览器自动携带者随机字符串,服务器用该字符串在session表里查找,查到结果就能确定你是谁了.但是当用户量很大的时候存储session表可是个大麻烦.

基于token原理认证:

和session类似,也是给你一个字符串,但不是随机的,而是用你的信息加密后的字符串,服务器拿到字符串后只需要反解信息就能获得你的信息,从而确定你是谁,服务器再也花费大量资金存session了,岂不美哉.

JWT认证:json web token是token认证的一种实现形式,在第7节详细介绍

认证功能的使用

  1. 写一个类继承BaseAuthentication,重写authenticate方法.认证通过返回user对象,否则抛一个异常

  2. 在需要认证的视图类里配置 authentication_classes=[认证类1,认证类2...]

drf还提供了配合Django的user表使用的认证权限类(登录认证权限需要配套使用),这里不再展开

全局使用,在setting.py中配置

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}

局部使用,写在视图类里,可以有多个认证,从左到右依次执行

authentication_classes=[MyAuthentication]

局部禁用

authentication_classes=[]

使用举例:使用session原理实现认证

# 写一个认证类 app_auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 认证逻辑,如果认证通过,返回两个值
        #如果认证失败,抛出AuthenticationFailed异常
        #虽然变量名叫token但实现原理是session原理,因为服务端存了随机字符串与用户关系表
        token=request.GET.get('token')
        if  token:
            
            #如果用户携带了token直接以token为过滤条件筛选,有结果token就是对了
            #这样通过token这个对象也能找到用户对象了
            user_token=UserToken.objects.filter(token=token).first()
            
            # 认证通过
            if user_token:
                return user_token.user,token
            #链表查询返回user对象,反向查询表名小写直接获得表对象
            else:
                raise AuthenticationFailed('认证失败')
        else:
            raise AuthenticationFailed('请求地址中需要携带token')

实现原理

源码分析:
认证代码执行路径:views-->APIView-->as_view-->dispatch-->self.initial-->self.perform_authentication-->request.user 
在此之前代码都在APIView中运行,执行了request.user之后,转到Request类
request.user -->self._authenticate方法
核心源码:
Request类中:
def _authenticate(self):

    for authenticator in self.authenticators: 
        try:
            user_auth_tuple = authenticator.authenticate(self) 
            #调用认证类对象的authenticate方法,该方法返回一个元祖或一个异常
            
        except exceptions.APIException:
            self._not_authenticated()
            raise

        if user_auth_tuple is not None:
            self._authenticator = authenticator
            #认证通过,将user,auth属性赋值给request对象
            self.user, self.auth = user_auth_tuple
            return

# self.authenticators的合理性

源码解析
#1,self.authenticators的authenticators从哪来的,它是什么?

#当前代码在Request类中,所以self代表request对象,为什么request对象有authenticators属性呢?
#authenticators是APIView在视图类获取配置的认证类之后调用生成的认证类对象,该属性在APIView通过initialize_request实例化request对象时赋值的,所以request对象具有authenticators属性.

APIView
    def initialize_request(self, request, *args, **kwargs):

        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
        )

#该方法的 self.get_authenticators
    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]


#2,重写的authenticate方法需要返回什么结果?
认证类的authenticate方法,认证通过返回一个元祖包含两个值,认证失败抛一个异常
认证通过:
   self.user, self.auth = user_auth_tuple 返回的第一个值会赋值个self.user,当前在Request类里,所以这两个值都给了request对象,即以后的request就有了这两个属性
       
认证失败:
    from rest_framework.exceptions import APIException,AuthenticationFailed
    抛一个AuthenticationFailed异常会被捕获

5.2 权限

权限需要和认证配合使用

确定了你是谁之后再检查你的权限,权限不够就不提供服务

自定义权限组件的使用

1)写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False

  1. 使用, 在需要认证的视图类里配置 permission_classes =[权限类1,权限类2...]

局部使用

class TestView(APIView):
    permission_classes = [app_auth.UserPermission]

全局使用

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
    'DEFAULT_PERMISSION_CLASSES': ['app01.app_auth.UserPermission',],
} 

局部禁用

class TestView(APIView):
    permission_classes = []
 
from rest_framework.permissions import BasePermission

class UserPermission(BasePermission):
    #重写has_permission,如果权限通过,就返回True,不通过就返回False
    def  has_permission(self, request, view):
        # 不是超级用户,不能访问
        # 由于认证已经过了,request内就有user对象了,当前登录用户
        user=request.user  # 当前登录用户
        
        # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文
        #print(user.get_user_type_display())
        
        if user.user_type==1:
            return True
        else:
            return False
        

实现原理

源码路径分析:
rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.check_permissions 一直在APIView这个视图类里

核心源码:
    def check_permissions(self, request):
        
        for permission in self.get_permissions(): #一个个由权限类组成的列表被调用生成对象
            if not permission.has_permission(request, self):#调用对象has_permission,获取结果
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
             
源码解析:
# 1)为什么认证需要到Request类中调用方法,权限不需?
    认证去Request调用方法是为了控制APIView的request能否有user这个对象属性以判断用户是否登录,	若用户登录则request对象就有了user对象属性,权限判断在认证之后执行,若用户登录了,request就有	user对象属性了,所以不用去Request类中调用方法.

# 2) self.get_permissions是什么?
	和认证的authenticator类似,get_permissions得到一个个由权限类组成的列表

# 3)重写的has_permission需要返回什么结果?
	学习一下源码的返回写法:
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_staff)
	
	布尔值,有权限就返回True,否则返回False


只使用user表配合自定义认证,权限类

Django的user表很好用,配合Django内置的登录认证权限效果更佳,但是对于复杂的业务逻辑需求,我们可以只使用user表.

认证,权限类还是自己定义,具体使用user表的时候需要使用user表提供的方法,user常用方法如下:

# 1.比对用户名和密码是否正确
user_obj = auth.authenticate(request,username=username,password=password)

# 2.保存用户状态
auth.login(request,user_obj)  
# 类似于request.session[key] = user_obj

# 3.判断当前用户是否登陆,返回布尔值
request.user.is_authenticated()

# 4.获取当前登陆用户
request.user
      
# 5.比对原密码,返回布尔值
request.user.check_password(old_password)

# 6.修改密码
request.user.set_password(new_password)  # 修改对象中的password属性
request.user.save()  # 操作数据库保存修改后的对象

# 7.注销
auth.logout(request) 

# 8.注册
# 操作auth_user表写入数据
from django.contrib.auth.models import User
# 创建普通用户
User.objects.create_user(username=username,password=password)
# 不能用create方法: create写入的密码是明文,没有加密处理

5.3 频率

限制某个接口单位时间内可被访问的频率

使用提供的频率类

drf提供的匿名用户频率类非常好用,所以我们一般只自定义限制登录用户的频率类

from rest_framework.throttling import BaseThrottle

AnonRateThrottle #限制匿名用户的频率类
UserRateThrottle #限制登录用户的频率类(必须是Django内置的登录认证)
ScopedRateThrottle #限制用户对于每个视图的访问频率类
SimpleRateThrottle #上面三个类都继承了该类,自定义频率类也继承它,重写get_cache_key

使用方法:配置了就能用

全局使用:settings的REST_FRAMEWORK里配置
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle', #限制匿名用户
        'rest_framework.throttling.UserRateThrottle', #限制登录用户(必须是Django内置的登录认证)
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/m',  #限制匿名用户的次数限制
        'user': '10/m', #登录用户的次数限制
    }
}
局部使用:视图类配置
    throttle_classes = [AnonRateThrottle,UserRateThrottle]
    

自定义频率类

1,写一个频率类继承SimpleRateThrottle,指明scop,必须重写get_cache_key方法,该方法返回用户相关的信息,或none

2,scope用与确定限制用户的类型,get_cache_key方法这里写业务逻辑,返回什么就根据什么限制.

3,配置,不再赘述

实现原理

源码路径分析:
rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.check_throttles 
核心源码    
	def check_throttles(self, request):
        throttle_durations = []
        for throttle in self.get_throttles():
            if not throttle.allow_request(request, self):
# 调用视图层配置频率类的allow_request方法,该方法将用户的信息存入缓存中,每次请求前判断是否超过访问频率
                throttle_durations.append(throttle.wait())

        if throttle_durations:
            duration = max(durations, default=None)
            self.throttled(request, duration)
posted @ 2020-07-06 22:23  Franciszw  阅读(175)  评论(0编辑  收藏  举报