使用步骤 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 视图类上局部配置和配置文件全局配置跟 权限一模一样