drf-认证、权限、频率
一、认证组件
1.认证组件的作用
- 写一个登录接口,保存用户登录状态
一些接口,想要限制登录之后才能访问,没登录不能访问
做登录认证,限制如果没有登录,不允许访问该接口
2. 认证类的使用:
1.在auth.py中 写一个类,去继承BaseAuthentication
2. 在这个类中重写:authenticate方法
3. 在authenticate完成登录认证,如果登录了,返回两个值:
- 当前登录的用户:user_token.user
- 前端传入的token:token
4. 如果没登录,抛异常:raise AuthenticationFailed('您没有登录')
5 认证类写好,在视图类中配置使用
1. 局部使用:配置在视图类上
2. 全局使用:在配置文件中配置,所有的接口都必须登录后才能使用
3. 在全局使用的情况下,局部禁用:在视图类中,authentication_classes=[ ]
前端传入的token,从哪里拿,怎么拿?
1. 在请求地址中拿:token = requset.qusery_params.get('token')
2. 在请求体中拿:token = request.data.get('token')
3. 在请求头中拿:token = request.META.get('HTTP_TOKEN')
2.1 以登录为例,做一个登录认证:
auth.py文件中:
from .models import UserToken from rest_framework.viewsets import ViewSetMixin from rest_framework.views import APIView from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed """ 1. 写一个类,继承BaseAuthentication 2. 在这个类中。重写authenticate方法 3. 在authenticate方法中完成登录认证,认证成功,返回两个参数,user_token.user,token 4. 认证失败,就抛出异常:AuthenticationFailed 会被drf捕获,处理,不会报错到前端 5. 在视图类中配置使用 """ class LoginAuth(BaseAuthentication): # 重写authenticate方法 def authenticate(self, request,*args,**kwargs): # 从前端拿到token值,与UserToken表中的token进行比较 token = request.query_params.get('token') user_token = UserToken.objects.filter(token=token).first() if user_token: return user_token.user,token else: raise AuthenticationFailed('您还没有登录')
from .models import User,UserToken from rest_framework.decorators import action from rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin,ViewSet from rest_framework.response import Response import uuid # Viewset(ViewSetMixin,APIView),继承这个类是因为,我们只查一条数据,不用查所有的数据,也不需要写序列化类 # 不需要自己写路由,可以自动生成路由 class UserView(ViewSet): # 写一个登录方法 @action(methods=['POST'],detail=False) def login(self,request,*args,**kwargs): # 从前端获取用户名和密码 username = request.data.get('username') password = request.data.get('password') user = User.objects.filter(username=username,password=password).first() # 如果有这用户,就生成一个随机字符串存入UserToken表中 if user: token = str(uuid.uuid4()) # 两中情况:一个是用户之前没登录过,直接存入token,二:用户之前登录过,就把之前的token改成新生成de # 方式一: # user_token = UserToken.objects.filter(user=user).first() # if user_token: # user_token.token=token # user_token.save() # 保存数据 # else: # UserToken.objects.create(token=token,user=user) # 方式二:通过user去UserToken表中查,如果能查到用defaults的更新,如果查不到,就用user和defaults新增一条记录 UserToken.objects.update_or_create(defaults={'token':token},user=user) return Response({'ccode':100,'msg':'登录成功','username':user.username,'token':token}) else: return Response({'code':101,'msg':'用户名或者密码错误'}) # 通过这个接口去判断有没有登录认证 from .auth import LoginAuth class UserAuthView(ViewSet): authentication_classes = [LoginAuth] def list(self,requset): print(requset.user.username) print(requset.auth) return Response('用户%s已登录'%requset.user.username)
路由配置:
postman:
从请求地址中获取token
从请求头中获取token
会自动将我们所写的字段转为大写,且加上HTTP_,token在取的时候,就变成了 request.META.get('HTTP_TOKEN')
总结:
1 认证类写好,使用
-配置在视图类上---》局部使用
-配置文件中配置--》全局使用---》所有接口都必须登录后才能用
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth'],
}
-局部禁用:
class UserView(ViewSet):
authentication_classes = []
2 写的认证类:要重写authenticate方法,必须返回两个参数:当前登录用户:user_token.user,用户的 token----》后续再视图类中:request.user 就是认证类返回的第一个参数,request.auth 就是认证类返回的第二个参数
3 如果认证失败,抛异常AuthenticationFailed,会被drf捕获,处理,不会报错到前端
4 前端传入的token,从哪取?
-后端定的,我们这个项目是从请求地址中取
-还可以从请求头或请求体中取
补充:上面所用的token原理和session的原理是一样的,本质原理也是session的认证机制
session的认证原理:
前端写的登录,会携带用户名和密码过来,然后视图函数的登录接口会获取用户名和密码,去数据库查,如果能查到就让用户登录,然后通过request.sessin['name'] = hh(通过request.session去保存登录状态和你想要保存的数据信息)
然后return返回,然后发生了四件事:1. 会生成一个随机字符串,2. 会把随机字符串和数据保存到django_session表中,3. 把随机字符串返回给浏览器,4. 浏览器将随机字符串保存到cookie中(以键值对的形式:sessionid:随机字符串),当你访问需要登录才能访问的接口时,浏览器会自动把cookie携带访问你的网址
然后中间件取出字符串,通过这个字符串去session表中去查询,把data的数据放入request.session中,形式是一个字典,然后就可以取出你前面保存的数据。
如图所示:
-签发阶段:做三件事:1 生成一个随机字符串 2 django-session表中插入记录 3 把随机字符串以cookie形式返回给前端(存在浏览器的cookie中)
-认证阶段:前端自动携带cookie到后端:sessionid:随机字符串
-django.contrib.sessions.middleware.SessionMiddleware
三大认证的执行:
self.initial(request, *args, **kwargs):
认证类的执行源码:
Request类中的user:
Request类中的_authenticate:
认证类可以配置多个,但是如果有一个返回了,后续的就不走了
self.authenticators 是request对象的属性,是在Request实例化的时候传入的,它什么时候实例化的,包装新的Reqeust时传入的---》APIView的dispatch-中找
二、 权限组件
在系统中,有普通用户,超级用户,超级管理员,他们都登录了,有的人有权限,就能访问这个接口
1. 使用的步骤:
1.在throttling.py文件中写一个类,继承Basepermission
2. 在这个类中,重写has_permission方法,在方法进行校验用户是否有权限,有权限,就返回True,没有权限,就返回False
- 权限的执行是在认证的后面,所以此时用户是已经登录的状态,就可以从requset.user中拿出当前登录的用户,对用户的类型进行判断
3. 在视图类中的使用:
- 配置在视图类中使用:进行局部配置:permission_classes = [UserPermission]
局部配置的时候要注意登录认证是全局配置还是局部配置,如果是全局配置,直接在需要权限的视图类中加permission_classes = [UserPermission],
如果是局部配置,需要在权限认证之前加上登录认证,不然就会出现一下的错误,request.user获取不到值
- 配置在配置文件中:全局使用:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['app01.auth.LoginAuth'],
'DEFAULT_PERMISSION_CLASSES': ['app01.permissions.UserPermission'],
}
- 全局配置了,但是某个接口不需要:局部禁用:permission_classes = [ ]
2. 代码执行:
# 进行权限认证 from rest_framework.permissions import BasePermission class UserPermission(BasePermission): def has_permission(self, request, view): # request 当次请求的request, 新的,它是在认证类之后执行的,如果认证通过了request.user 就是当前登录用户 # 拿到当前登录用户,查看它的类型,确定有没有权限 if request.user.user_type == 3: return True else: self.message = '您的用户类型是:%s,您没有权限操作' % (request.user.get_user_type_display()) return False
views中:
class UserPermissionView(ViewSet): authentication_classes = [LoginAuth] permission_classes = [UserPermission] # 局部使用,必须在登录认证的基础上 def create(self, request,*args,**kwargs): return Response('您新增了一条记录')
结果:
权限类的源码:self.check_permissions(request)
权限类对象的列表:
三、频率组件
作用:对接口进行访问次数限制,控制一个接口,访问频次,比如一分钟只能访问1次
1. 使用步骤:
1. 在throllting.py文件中,写一个类继承SimpleRateThrottle
2. 在这个类中,重写get_cache_key方法,返回什么,就以什么做限制:
-1. IP地址,当用户没有登录的时候,以他的IP地址做限制,返回他的IP号
-2. 用户id限制,当用户登录了,就以他的id号进行限制,返回他的id号
3. 写一个类属性 scope = 'drf_day8'
4. 在配置文件中配置:
'DEFAULT_THROTTLE_RATES': {'drf_day08': '3/m', # 一分钟访问三次},
5 局部使用,全局使用,局部禁用
频率类的源码分析:
在APIView的dispatch方法中执行了频率,
在initial的方法中执行了:self.check_throttles(request)
check_throttles方法:中执行了allow_request方法
频率类要写:
1. 写一个类,继承BaseThrottle类
2. 在类中重写:allow_request方法,传入3个参数
3. 在allow_request方法中写限制逻辑,如果还能访问,就返回True
4. 如果超过我们事先设定的访问次数,就不能访问了,返回False
5. 局部配置在视图类上
6. 全局配置在配置文件中
自定义的频率类:
from rest_framework.throttling import BaseThrottleclass MyThrottle(BaseThrottle): # VISIT_RECORD = {'192.168.1.1':[当前时间,当前时间,访问时间列表]} VISIT_RECORD = {} def __init__(self): self.history = [] def allow_request(self, request, view): ip = request.META.get('REMOTE_ADDR') import time ctime = time.time() # (2)判断当前ip不在访问字典里,说明是第一次访问,添加进去,并且直接返回True,表示第一次访问 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [ctime, ] # VISIT_RECORD = {'192.168.1.1':[当前时间2,当前时间1,]} return True # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, self.history = self.VISIT_RECORD[ip] # 访问时间列表 while self.history and ctime - self.history[-1] > 60: # 循环删除1分钟之前访问的实际 self.history.pop() # 最后self.history都剩下是一分钟之内的时间了 # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过 # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败 if len(self.history) < 3: self.history.insert(0, ctime) return True else: return False def wait(self): import time ctime = time.time() return 60 - (ctime - self.history[-1])
我们在drf中写的时候,不需要继承BaseThrottle,而是继承了SimpleRateThrottle,重写了get_cache_key方法,所以SimpleRateThrottle帮我们写了我们需要写的allow_request方法
SimpleRateThrottle源码分析:
rom rest_framework.throttling import SimpleRateThrottle 从这里进去看
def parse_rate(self, rate):
allow_request方法:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY