认证权限频率
认证
自定义认证
# 登录认证即某个接口必须登录后才能访问
登录接口 ----> 登录成功返回随机字符串 ----> 携带随机字符串【认证】 通过 ----> 再继续访问某个接口
三大认证在视图类的方法之前执行的
# 写一个登录接口
-用户表,用户token表
-前端传入用户名密码----视图类----登录方法----校验用户名密码是否正确,如果正确,生成随机字符串存入数据库----把随机字符串返回给前端
# 随便写个接口(登录后才能访问)
# 写认证类(新建auth.py)
-第一步:写一个类,继承BaseAuthentication
-第二步:在类中重写authenticate方法
-第三步:在方法中做验证,如果通过,返回两个值,如果不通过,抛AuthenticationFailed的异常
# 使用认证类
-局部使用-->视图类中
class BookView(APIView):
authentication_classes = [LoginAuth,]
-全局使用---》配置文件
REST_FRAMEWORK = {
# 全局使用认证类
'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth',]
}
-局部禁用
class BookView(APIView):
authentication_classes = []
# 前端调用接口时
http://127.0.0.1:8000/user/books?token=27954a0e-9b0b-442d-8aff-839ec9f70960
详细代码
模型类
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
class UserToken(models.Model):
token = models.CharField(max_length=32) # 用户登录成功签发的随机字符串
user = models.OneToOneField(to='User',on_delete=models.CASCADE)
路由
from rest_framework.routers import SimpleRouter
from .views import UserView,BookView
router=SimpleRouter()
router.register('user',UserView,'user')
urlpatterns = [
]
urlpatterns+=router.urls
视图类
class UserView(ViewSet):
authentication_classes = [] # 局部禁用
# 127.0.0.1:8080/user/user/login--->post
@action(methods=['post', ], detail=False)
def login(self, request):
# 取出用户名密码----去数据库查询----有就登录成功(生成随机字符串存入数据),没有就登录失败
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 登录成功
# 生成一个随机字符串----uuid模块----生成一个随机不重复的值
token = str(uuid.uuid4()) # python 动态强类型
# 存到UserToken表中----两种情况:之前有了就更新,之前没有就新增
# 有就更新,没有就修改
# defaults=None, **kwargs:传入的去查询,如果能查到使用defaults给的更新
UserToken.objects.update_or_create(user=user, defaults={'token': token})
# UserToken.objects.update_or_create( defaults={'token': token},kwargs={'user':user})
return Response({'code': 100, 'msg': '登录成功', 'token': token})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 做验证,验证用户是否登录
# 放到请求地址中/请求头中
token=request.query_params.get('token')
# 去数据库查 token在不在
user_token=UserToken.objects.filter(token=token).first()
if user_token:
# 验证通过了,返回两个值,第一个值是当前登录用户,第二个值是token
return user_token.user,token
else:
# 验证失败
raise AuthenticationFailed('小伙子,您没有登录,不能访问')
权限
# 登录成功了,但是有的接口区分权限,有的人有权限,有的人没权限
# 用户表新增一个 用户类型字段:超级管理员,普通管理员,普通用户
# 权限类的写法
-第一步:写一个类,继承BasePermission
-第二步:重写has_permission方法
-第三步:在has_permission中,进行权限的判断,如果有权限,返回True,如果没有权限,返回False,返回的中文提示信息,使用message字段标识
#权限类的使用
-局部使用-->视图类中
class BookView(APIView):
permission_classes = [PermissionAuth,]
-全局使用---》配置文件
REST_FRAMEWORK = {
# 全局使用认证类
'DEFAULT_PERMISSION_CLASSES':['app01.auth.PermissionAuth',]
}
-局部禁用
class BookView(APIView):
permission_classes = []
详细代码
# 权限类
from rest_framework.permissions import BasePermission
class PermissionAuth(BasePermission):
message='你不是超级用户,不能访问'
def has_permission(self, request, view):
# 判断权限----由于权限的执行在认证后执行,到这说明已经认证通过了,认证通过了request.user就是当前登录用户
if request.user.user_type ==1:
# 如果有权限,return True
return True
else:
# 用户类型中文
self.message='你是:%s,不能访问'%request.user.get_user_type_display()
# 如果没有有权限,return False
return False
频率
某个接口,可以根据IP,用户id,限制访问频率
# 频率类的编写
-第一步:写一个类,继承SimpleRateThrottle
-第二步:重写get_cache_key方法
-第三步:返回什么,就以什么做限制
-第四步:写一个类属性
-scope = '3_min'
-第五步:配置文件中配置
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES": {
# 频率类中scope对应的值
'3_min': '3/m' # 数字/s m h d
},
# 'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]
}
-第六步:局部和全局使用
局部用:视图类中
class BookView(APIView):
throttle_classes = [IPThrottle, ]
全局用:配置文件
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES": {
# 频率类中scope对应的值
'3_min': '3/m' # 数字/s m h d
},
'DEFAULT_THROTTLE_CLASSES':['app01.throttling.IPThrottle',]
}
详细代码
频率类
from rest_framework.throttling import SimpleRateThrottle
class IpThrottle(SimpleRateThrottle):
# 写一个类属性,字符串
scope = '3_min'
def get_cache_key(self, request, view):
# return 什么就以什么做限制, 返回ip 返回用户id
return request.META.get('REMOTE_ADDR')
# return request.user.id # 返回用户id
视图类
在你需要限制频率的视图中
def throttled(self, request, wait):
from rest_framework.exceptions import Throttled
class MyThrottled(Throttled):
default_detail = '超限制乐'
extra_detail_singular = '还有 {wait} 描述.'
extra_detail_plural = '出了 {wait} 秒.'
raise MyThrottled(wait)
配置文件
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES": {
# 频率类中scope对应的值
'3_min': '3/m', # 数字/s m h d
'anon':'4/h',
'user':'5/m'
},
}
drf内置认证类,权限类,频率类
内置认证类
-SessionAuthentication:之前老的session认证登录方式用,后期都不用了
-BasicAuthentication :基本认证方式,咱们不用
-TokenAuthentication :使用token认证方式,有用,但是咱们也是自己写的
内置权限类
-IsAdminUser :校验是不是auth的超级管理员权限
-IsAuthenticated:后面会用到,验证用户是否登录了,登录了才有权限,没登录就没有权限
-IsAuthenticatedOrReadOnly:知道有这个东西即可
内置频率类
-UserRateThrottle :限制登录用户的频率,需要配置配置文件
-AnonRateThrottle:登录用户不限制,未登录用户限制,需要配置配置文件
认证类源码分析
####### 不要无脑按住ctrl建点击查找,因为方法,属性的查找顺序是按照 mro列表查找顺序来的
# 研究第一个点:三大认证的执行顺序---》入口---》APIView的dispathc----》三大认证
-dispatch中第 497行 --》self.initial(request, *args, **kwargs)
-self是谁?self是个对象,是谁的对象? 是视图类的对象---》BookView这些视图类
-APIView的initial方法的第414行
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
# 为什么写了认证类,配置在视图类上,就会走认证?---》入口-->self.perform_authentication(request)
-第一步:self.perform_authentication源码
def perform_authentication(self, request):
request.user
-第二步:新的request类中 user(属性?方法?)
from rest_framework.request import Request
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate() # 最开始,是没有_user的,执行这句
return self._user
-第三步:self._authenticate() --》self是Request的对象---》Request的_authenticate方法
def _authenticate(self):
# authenticator配置的认证类的对象
for authenticator in self.authenticators: # 你配置在视图类上authentication_classes = [你写的认证类,]----》[你写的认证类(),你写的认证类2()]
try:
# 调用认证类对象的authenticate方法,传了几个?2个,一个认证类自己,一个是self:Request类的对象
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException: # 抛的是AuthenticationFailed,捕获的是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
-第四步: self.authenticators到底是啥---》你配置在视图类上authentication_classes = [你写的认证类,]----》[你写的认证类(),你写的认证类2()]
-Reqeust这个类实例化的时候,传入的,如果不传就是空元组
-找Request的实例化---》dispatch中包装了新的Request
-request = self.initialize_request(request, *args, **kwargs)
-authenticators=self.get_authenticators(),
-APIView中get_authenticators---》268行
def get_authenticators(self):
# self.authentication_classes配在视图类上的认证类列表---》认证类对象列表
return [auth() for auth in self.authentication_classes]
排序
排序功能接口只针对于:获取所有接口
# 继承了GenericAPIView的视图类,只需要要加入两个类属性
class BookView(GenericViewSet, ListModelMixin):
serializer_class = BookSerializer
queryset = Book.objects.all()
filter_backends = [OrderingFilter, ]
ordering_fields = ['price','id' ]
# 访问的时候
http://127.0.0.1:8000/api/v1/books?ordering=price # 按price升序
http://127.0.0.1:8000/api/v1/books?ordering=-price # 按price降序
http://127.0.0.1:8000/api/v1/books?ordering=price,id # 先按价格升序排,价格一样,再按id升序排
过滤
使用于:获取所有接口
# 内置的过滤使用(不能指定查询那个字段,模糊查询)
-继承了GenericAPIView的视图类,只要加入两个类属性
class BookView(GenericViewSet, ListModelMixin):
serializer_class = BookSerializer
queryset = Book.objects.all()
filter_backends = [SearchFilter,]
search_fields=['name','price'] # 按name或price过滤
-使用
http://127.0.0.1:8000/api/v1/books?search=金
# search= xx ----》具体查询是 name like xx or price like xx
# 我们想实现http://127.0.0.1:8000/api/v1/books?name=金&price=12----使用第三方django-filter
-安装:pip3 install django-filter
-继承了GenericAPIView的视图类,只要加入,两个类属性
from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
serializer_class = BookSerializer
queryset = Book.objects.all()
filter_backends = [DjangoFilterBackend, ]
filter_fields = ['name', 'price']
-使用
http://127.0.0.1:8000/api/v1/books?name=金&price=11
# 自定义过滤器---》完成更多查询操作
-写一个类,继承BaseFilterBackend
-重写filter_queryset方法
from rest_framework.filters import BaseFilterBackend
class FilterName(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 返回的数据,就是过滤后的数据
# 从请求地址中去除过滤条件,对queryset进行过滤,返回
name = request.query_params.get('name')
return queryset.filter(name__contains=name)
-配置在视图类中
from .throttling import FilterName
class BookView(GenericViewSet, ListModelMixin):
serializer_class = BookSerializer
queryset = Book.objects.all()
filter_backends = [FilterName, ]
# 既有过滤又有排序
class BookView(GenericViewSet, ListModelMixin):
serializer_class = BookSerializer
queryset = Book.objects.all()
filter_backends = [FilterName, OrderingFilter]
ordering_fields = ['price', ]
# 源码分析,为什么这么配置就生效
-GenericAPIView的方法
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset