本文主要是介绍DRF 三大认证,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
三大认证使用
【0】准备
(1)APIView源码回顾
- APIView不用csrf验证了
- APIView的request方法不在是以前的request方法,有了很多好用的新方法
- APIView内部还分别进行了了用户、权限、频率的验证
- 三大认证之后才执行的视图层的方法
(2)模型表创建
from django.db import models# 图书表
class Book(models.Model):name = models.CharField(max_length=64, verbose_name="书名")price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="价格")publish_name = models.CharField(max_length=64, verbose_name="出版社名字")# 用户表
class User(models.Model):name = models.CharField(max_length=64, verbose_name='用户名')password = models.CharField(max_length=64, verbose_name='密码')role = models.IntegerField(choices=((0, "Admin"), (1, 'Normal')), verbose_name='用户名')# 用户token表
class UserToken(models.Model):token = models.CharField(max_length=64, verbose_name='token')user = models.OneToOneField(to=User, on_delete=models.CASCADE, verbose_name="关联用户表")
【1】认证组件
(1)简介
- 三大认证之首,只有通过这个认证才会进行两外两个认证
- 认证组件用于确定请求是来自谁。例如,它可以检查一个请求是否附带了一个有效的用户会话token,从而识别出请求的用户。
(2)使用方法
-
编写一个继承
BaseAuthentication
的类from rest_framework.authentication import BaseAuthentication
-
重写
authenticate
方法- 可以在
BaseAuthentication
类中看到authenticate() must be overridden
的提示,提示我们要重写
- 可以在
-
重写方法中进行特殊内容的判断(这里是token)
- token是后端开发者对前端传输数据进行要求的,可以指定内容,可以指定格式,由于无论什么请求方式,都会发送请求头,所以可以指定数据放在请求头中
- 请求通过,直接返回用户对象和token
- 请求失败,抛异常AuthenticationFailed
from rest_framework.exceptions import AuthenticationFailed
- 例如:
raise AuthenticationFailed("请先登录再重试。")
-
配置使用
-
局部使用
- 在试图内中添加
authentication_classes = [自定义认证类列表]
,这是个列表,所以说明可以添加多个认证类
- 在试图内中添加
-
全局使用
-
直接去
drf
的setting
配置文件中找 -
REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ['字符串形式导入自定义认证类',], }
-
-
局部禁用
- 再进行全局配置即所有视图类都需要登录认证以后
- 根据就近优先原则,只要将当前的视图类中
authentication_classes
置空就可以取消认证,即局部禁用
-
(3)示例
- 路由层:使用继承
ViewSetMixin
的视图类,所以直接自动生成路由
from django.urls import path, include
from .views import UserAPIView
from rest_framework.routers import SimpleRouterrouter = SimpleRouter()
router.register(prefix='user', viewset=UserAPIView, basename='user')
router.register(prefix='book', viewset=BookAPIView, basename='book')
urlpatterns = [path('', include(router.urls))
]
- 视图函数登录功能
- 注册就直接再数据库添加了
update_or_create(defaults={'token': token}, user=user_obj)
讲解- defaults参数是一个字典,需要添加的所有字段信息(除了后面的那个字段)
- 这里的字段是user,即根据user进行判断,这个数据是已经添加过的还是新的,如果是新的那么就是创建,如果是旧的数据,那么就进行更新
# 登录功能
import uuid
from rest_framework.viewsets import ViewSet
from .models import User, UserToken
from rest_framework.response import Response
from rest_framework.decorators import actionclass UserAPIView(ViewSet):@action(methods=['post'], detail=False)def login(self, request):username = request.data.get('username')password = request.data.get('password')user_obj = User.objects.filter(username=username, password=password).first()if user_obj:token = uuid.uuid4()UserToken.objects.update_or_create(defaults={'token': token}, user=user_obj)return Response({"code": 100, "msg": "登录成功", "token": token})return Response({"code": 101, "msg": "登录失败,用户名或密码错误"})
# 书籍查询所有,添加单本
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from .serializer import BookModelSerializer
from .authentication import LoginAuthclass BookAPIView(GenericViewSet, ListModelMixin, CreateModelMixin):queryset = Book.objects.all()serializer_class = BookModelSerializer# 认证类authentication_classes = [LoginAuth]# 序列化类
from rest_framework.serializers import ModelSerializer
from .models import Bookclass BookModelSerializer(ModelSerializer):class Meta:model = Bookfields = '__all__'
- 认证类编写
- 需要注意的是,前端的数据是放在请求头中的token,再后端需要加上HTTP_大写变量取值
- 最后成功返回
user
和token
- 不成功抛出异常
AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserTokenclass LoginAuth(BaseAuthentication):def authenticate(self, request):token = request.META.get("HTTP_TOKEN")token_obj = UserToken.objects.filter(token=token).first()if token_obj:return token_obj.user, tokenraise AuthenticationFailed("请先登录再重试。")
- 试图函数测试:书籍信息查询功能
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from .serializer import BookModelSerializer
from .authentication import LoginAuthclass BookAPIView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookModelSerializerauthentication_classes = [LoginAuth]
- 不登陆测试
- 登录后添加token测试
【2】权限组件
(1)简介
- 三大认证的第二个,到这里说明已经通过了认证组件
- 权限组件用于确定已认证的用户是否有权限执行某个操作。例如,一个API视图可能只允许特定的用户组或具有特定权限的用户访问。
(2)使用方法
-
编写一个继承
BasePermission
的类from rest_framework.permissions import BasePermission
-
重写
has_permission
方法- 根据自定义规则,通过返回True,失败返回False
- 验证不通过自定义返回信息f方法,抛出异常AuthenticationFailed
from rest_framework.exceptions import PermissionDenied
- 例如:
raise PermissionDenied(f'你是【{role_info}】,没有权限执行当前操作。')
-
配置使用
-
局部使用
- 在试图内中添加
permission_classes = [自定义权限类列表]
,这是个列表,所以说明可以添加多个权限类
- 在试图内中添加
-
全局使用
-
直接去
drf
的setting
配置文件中找 -
REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': ['字符串形式导入自定义认证类',], }
-
-
局部禁用
- 再进行全局配置即所有视图类都需权限认证以后
- 根据就近优先原则,只要将当前的视图类中
permission_classes
置空就可以取消权限认证,即局部禁用
-
(3)示例
-
注意:前面必须要有认证类通过认证才可以即一定要有
authentication_classes
,因为这里的权限认证是从登录后的信息拿取权限的内容 -
权限类编写
- 根据自定义规则进项权限检查
- 最后成功返回True
- 不成功抛出异常
PermissionDenied
- 由于User表是自定义的没有继承
django
的用户表,所以是没有is_authenticated
方法,需要再模型表中自定义,返回个True即可,表示是这个表创建的
# 权限
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import PermissionDeniedclass AdminPermission(BasePermission):def has_permission(self, request, view):# 检查是否登录if request.user.is_authenticated:# 检查权限只有管理员才可以if request.user.role == 0:return Truerole_info = request.user.get_role_display()raise PermissionDenied(f'你是【{role_info}】,没有权限执行当前操作。')raise PermissionDenied(f'请先登录执行当前操作。')# 模型表
class User(models.Model):username = models.CharField(max_length=64, verbose_name='用户名')password = models.CharField(max_length=64, verbose_name='密码')role = models.IntegerField(choices=((0, "Admin"), (1, 'Normal')), verbose_name='用户名')@propertydef is_authenticated(self):return True
- 视图函数测试:
- 在添加一个书籍对单本图书的视图函数,使用路由注册视图集时,每个视图集通常应该对应一个特定的 URL 前缀,以避免冲突。
# 路由层
from django.urls import path, include
from .views import UserAPIView, BookAPIView, BookOneAPIView
from rest_framework.routers import SimpleRouterrouter = SimpleRouter()
router.register(prefix='user', viewset=UserAPIView, basename='user')
router.register(prefix='books', viewset=BookAPIView, basename='books')
router.register(prefix='book', viewset=BookOneAPIView, basename='book_one')
urlpatterns = [path('', include(router.urls))
]# 视图层
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, \DestroyModelMixin
from .serializer import BookModelSerializer
from .authentication import LoginAuth
from .permission import AdminPermissionclass BookAPIView(GenericViewSet, ListModelMixin, RetrieveModelMixin):queryset = Book.objects.all()serializer_class = BookModelSerializerauthentication_classes = [LoginAuth]permission_classes = []class BookOneAPIView(GenericViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):queryset = Book.objects.all()serializer_class = BookModelSerializerauthentication_classes = [LoginAuth]permission_classes = [AdminPermission]
- 普通用户测试
- 管理员测试
【3】频率组件
(1)简介
- 三大认证的最后一个,到这里说明已经通过了认证和权限组件
- 权限组件用于确定已认证的用户是否有权限执行某个操作。例如,一个API视图可能只允许特定的用户组或具有特定权限的用户访问。
(2)SimpleRateThrottle
使用方法
-
编写一个继承
SimpleRateThrottle
的类from rest_framework.throttlingimport SimpleRateThrottle
-
重写
get_cache_key
方法- 可以在
SimpleRateThrottle
类中看到get_cache_key() must be overridden
的提示,提示我们要重写 - 在类属性中定义一个rate属性
- 参数形式为:
3/m
,这里表示一分钟内最多访问三次 - 所以第一个参数是次数,
/
后面的是单位,s
是秒,m
是分钟,h
是小时,实际上写全称也可以,只和首字母有关,源码导致
- 参数形式为:
- 这个方法需要返会限制频率的唯一标识
- 返回
request.META.get('REMOTE_ADDR')
表示IP
- 返回
- 可以在
-
配置使用
-
局部使用
- 在试图内中添加
throttle_classes= [自定频率类列表]
,这是个列表,所以说明可以添加多个频率类
- 在试图内中添加
-
全局使用
-
直接去
drf
的setting
配置文件中找 -
REST_FRAMEWORK = {'DEFAULT_THROTTLE_CLASSES': ['字符串形式导入自定义频率类',], }
-
-
局部禁用
- 再进行全局配置即所有视图类都限制频率以后
- 根据就近优先原则,只要将当前的视图类中
throttle_classes
置空就可以取消频率限制,即局部禁用
-
(3)示例
- 频率类编写
- 重写
get_cache_key
方法,返回IP - 添加类属性
rate
- 重写
from rest_framework.throttling import BaseThrottle, SimpleRateThrottleclass CommonThrottle(SimpleRateThrottle):rate = '3/m'def get_cache_key(self, request, view):return request.META.get('REMOTE_ADDR')
- 视图函数测试:
class BookAPIView(ReadOnlyModelViewSet):queryset = Book.objects.all()serializer_class = BookModelSerializerauthentication_classes = [LoginAuth]permission_classes = []throttle_classes = [CommonThrottle]
(4)BaseThrottle
使用方法
-
和
SimpleRateThrottle
的使用方法差不多,不过要麻烦很多,几乎要完全自定义,需要重写allow_request
方法 -
补充一点:
- 正常来说,通过返回True,不通过返回False
- 但是不通过可以自定义返回信息,使用
Throttled
实例化一个信息,并抛出这个异常。from rest_framework.exceptions import Throttled
from rest_framework.throttling import BaseThrottle
from rest_framework.exceptions import Throttledclass ComplexThrottle(BaseThrottle):login_dict = {} # 登录信息字典frequency = 3 # 时间内访问次数interval = 10 # 时间间隔 sdef allow_request(self, request, view):print(self.login_dict)now_ip = request.META.get('REMOTE_ADDR')now_time = time.time()# 是否是新用户if now_ip not in self.login_dict.keys():self.login_dict[now_ip] = [now_time]return Trueelse:# 时间间隔dif = now_time - self.login_dict.get(now_ip)[0]# 时间间隔大于指定间隔if dif > self.interval:self.login_dict.get(now_ip).pop(0)self.login_dict.get(now_ip).append(now_time)return True# 存储超过指定数量不在添加elif len(self.login_dict.get(now_ip)) >= self.frequency:# 自定义返回内容raise Throttled(detail={'msg': f"访问频率过高,请等待{self.interval - int(dif)}秒后再试。"})# 最后说明:在时间间隔内,且有位置可以添加else:self.login_dict[now_ip].append(now_time)return True
这篇关于DRF 三大认证的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!