url:
url(r'books/$',views.BookView.as_view({ 'get':'list','post':'create'})) 为例
当django启动的时候,会调用执行view.BookView.as_view()方法,拿到as_view的返回值view
url(r'books/$',View.view)
当用户访问books/的时候,会执行View.view(). 执行APIView.dispatch()
当用户访问books/时,django拿到request,然后对request进行了重新封装 并且进行用户认证
url(r'books/$',View.view) ---> 执行 self.dispatch()
APIView.dispatch()
def dispatch(self, request, *args, **kwargs): ... request = self.initialize_request(request, *args, **kwargs) self.request = request try: self.initial(request, *args, **kwargs) ....
request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs): #返回一个Request对象 return Request( ... authenticators=self.get_authenticators(), ... ) def get_authenticators(self): # 循环视图类authentication_classes # 使用列表推导式,生成每一个实例对象 return [auth() for auth in self.authentication_classes]
回到dispatch()中,继续向下执行self.inital(request,*args,**kwags)
def initial(self, request, *args, **kwargs): ... # 认证 self.perform_authentication(request) # 权限 self.check_permissions(request) # 访问频率限制 self.check_throttles(request)
对于用户认证,我们主要看 self.perform_authentication(request) 干了什么
def perform_authentication(self, request): # 调用了request.user 属性方法 request.user
@propertydef user(self): if not hasattr(self, '_user'): with wrap_attributeerrors(): # 调用 _authenticate 此时的self是request对象 self._authenticate() return self._userdef _authenticate(self): # 此时的self是request ,authenticator每一个验证类对象 for authenticator in self.authenticators: try: # 使用authenticator调用它的authenticate() 返回一个元组 #元组的第一个值是request.user,第二个值是request.auth user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: # 如果没有验证通过,抛出异常 self._not_authenticated() raise # 如果 user_auth_tuple 不为空的话,证明authenticator.authenticate(self) 返回了数据 # **** 有多个验证类的话,最后一个认证类返回值,其他验证类不要返回 # 否则,这个验证类之后的所有验证类都不会被执行 # 如果返回值不为None,赋值后 将会跳出循环 if user_auth_tuple is not None: self._authenticator = authenticator # 进行赋值 self.user, self.auth = user_auth_tuple # 跳出for循环, 验证通过 return self._not_authenticated()
认证执行流程:
1. 查看settings.py中是否配置了作用于全局的REST_FRAMEWORK配置,如果有,就是用全局
2. 如果settings.py没有配置,就去视图类中(本例是BookView)查看是否配置了authentication_classes
3. 以上都没有配置,就是用默认的,在APIView中配置
自定义一个认证
class BookAuthen(): def authenticate(self,request): #获取token,检验token是否正确,如果正确则,验证通过 #否则抛出异常 token_val = request.GET.get('token') user_obj = models.User.objects.filter(token__token=token_val).first() if user_obj: return user_obj.name,token_val else: raise exceptions.AuthenticationFailed def authenticate_header(self,request): passclass BookView(ModelViewSet): # 如果提供,就执行BookAuthen,如果没有提供,使用APIView提供的 authentication_classes = [BookAuthen,] queryset = models.Book.objects.all() serializer_class = BookModelSerializer
全局配置(settings.py):
REST_FRAMEWORK= { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'bookmanage.auth.authen.BookAuthen', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication' ) }
默认配置(APIView):
class APIView(View): ... # 首先api_settings没有DEFAULT_AUTHENTICATION_CLASSES 所以会调用apisetting.__getattr__() authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES ...
二、访问频率限制
访问频率限制和用户认证流程几乎相同
首先看url
url(r'books/$',views.BookView.as_view({ 'get':'list','post':'create'})) 为例 django启动时 去执行BookView.as_view()方法--->APIView.as_view()-->最终返回的是APIView.view url(r'books/$',views.BookView.as_view({ 'get':'list','post':'create'})) url(r'books/$',APIView.view) 用户访问时
执行APIView.view(),其中它调用了父类的as_view(),实际上什么都没做 执行self.dispatch(),执行的是APIView.dispatch() self.initial(request, *args, **kwargs) 调用了 --> self.check_throttles(request) #此时的self是BookView视图类
dispatch()中,self.inital(request,*args,**kwags) --> self.check_throttles(request)
def get_throttles(self): """ 遍历视图类中的throttle_classes 拿到每一个自定义访问频率限制类的实例对象 """ return [throttle() for throttle in self.throttle_classes] def check_throttles(self, request): for throttle in self.get_throttles(): # 拿到自定义访问频率限制类的实例对象,调用allow_request() if not throttle.allow_request(request, self): # 如果没有通过验证,执行self.throttled()抛出异常 self.throttled(request, throttle.wait())
自定义访问频率限制类
# 限制IP频繁访问 class VisitThrottle(object): detail = "访问频率不正常" def allow_request(self,request, obj): now_time = str(time.time()) if request.session.get('visittime'): visited = request.session.get('visittime') visited.append(now_time) request.session['visittime'] = visited if len(visited) >= 20: first = visited[-2] last = visited[-1] time_ = time.gmtime(float(last) - float(first)) print(time_) if time_.tm_min > 1: request.session['visittime'] = [] return True elif time_.tm_min <= 1: raise exceptions.Throttled(detail=self.detail) else: request.session['visittime'] = [now_time,] print(request.META.get('REMOTE_ADDR')) return True class BookView(ModelViewSet): #在视图类中配置自定义频率访问组件 throttle_classes = [VisitThrottle] queryset = models.Book.objects.all() serializer_class = BookModelSerializer
默认访问频率限制
由于每一个视图类都是继承自APIView,如果配置throttle_classes,就使用自定义的如果没配置throttle_classes,APIView配置了默认的
class APIView(View): .... # 由于api_settings没有DEFAULT_THROTTLE_CLASSES,会调用api_setting的__getattr__() throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES .... api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) class APISettings(object): def __init__(self, user_settings=None, defaults=None, import_strings=None): #不会进入 if user_settings: self._user_settings = self.__check_user_settings(user_settings) # 将DEFAULTS 复制给 self.defaults self.defaults = defaults or DEFAULTS @property def user_settings(self): if not hasattr(self, '_user_settings'): self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) return self._user_settings def __getattr__(self, attr): if attr not in self.defaults: raise AttributeError("Invalid API setting: '%s'" % attr) try: # self.user_settings : 它从settings.py中取找 是否存在 REST_FRAMEWORK配置项 # 如果有,因为他是一个字典,取[DEFAULT_THROTTLE_CLASSES]对应的路径自定义访问频率类 val = self.user_settings[attr] except KeyError: # 如果没有,就使用默认的 # self.defaults[DEFAULT_THROTTLE_CLASSES] val = self.defaults[attr] .... return val
全局配置
1. 此时,应该将自定义的访问频率组件提取出来单独放在一个py文件中 2. 指定路径类 3. 所有的url访问都会进行访问频率限制
REST_FRAMEWORK= { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'bookmanage.auth.authen.BookAuthen', ), 'DEFAULT_THROTTLE_CLASSES':( 'bookmanage.auth.throttle.VisitThrottle' ) }