路由和认证

路由

1 三种路由的配置

在urls.py里面的配置的常规配置

path('books4/', views.Book4View.as_view()),
re_path('books4/(?P<pk>\d+)', views.Book4DetailView.as_view())

视图类中继承了ViewSetMixin

path('books5/', views.Book5View.as_view(actions={'get':'list','post':'create'})), #当路径匹配,又是get请求,会执行Book5View的list方法
re_path('books5/(?P<pk>\d+)', views.Book5View.as_view(actions={'get':'retrieve','put':'update','delete':'destroy'})),

继承视图类ModelViewSet路由的写法

  • 配置路由导入模块

    from  rest_framework import routers
    
  • 两个类,实例化得到对象

    routers.DefaultRouter
    ····
    ^books/$ [name='book-list'] # 根
    ^books\.(?P<format>[a-z0-9]+)/?$ [name='book-list']# simple的差不多
    ^books/(?P<pk>[^/.]+)/$ [name='book-detail']
    ^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='book-detail']# 和simple的差不多
    ^$ [name='api-root']# 根路径会显示出所有可以访问的地址
    ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']
    ····
    自动生成的6个路由
    routers.SimpleRouter
    
    """[<RegexURLPattern book-list ^books/$>,
    <RegexURLPattern book-detail ^books/(?P<pk>[^/.]+)/$>]"
    
    """
    自动生成的2个路由
    
  • 注册

    router.register('books',views.BookViewSet)# 后面可以加别名
    
  • 自动生成路由,加入到原来的路由中去

    urlpatterns+=router.urls
    

    action的使用

    是为了给继承ModelViewSet视图类定义的函数也添加路由

    class BookViewSet(ModelViewSet):
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer
    
        @action(methods=['GET','post'], detail=True)
        def get_5(self, request,pk):
            book = self.get_queryset()[:5]  # 从0开始截取
            ser = self.get_serializer(book, many=True)
            return Response(ser.data)
      
    
  • methods:第一个参数,传一个列表,列表中放请求方式,如get,post等

  • detail:

    ^books/get_1/$ [name='book-get-1'] 当向这个地址发送get请求,会执行下面的函数
    detail:布尔类型 如果是True
    ^books/(?P<pk>[^/.]+)/get_1/$ [name='book-get-1']
    http://127.0.0.1:8000/books/1/get_5/
    

认证

1 认证的实现

1 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过,返回两个值,一个值最终给了Requet对象的user,认证失败,抛异常:APIException或者AuthenticationFailed
2 全局使用,局部使用

2 drf认证的源码分析

入口点

在APIView-->  as_view--> dispatch方法--> self.initial(request, *args, **kwargs)
这个方法中有认证,权限,频率

dispatch方法源码

    def dispatch(self, request, *args, **kwargs):
       
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs) 

            # Get the appropriate handler method
            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)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

在try里面 self.initial这个方法点进去,在APIView里面

initital方法

  def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)

self.perform_authentication(request)只读这个认证的功能 ,传个request过来,这个request是包装后的,

perform_authentication方法的源码

    def perform_authentication(self, request):
        request.user

因为这个是包装过后的Request,所以我们要到包装过后的request对象里面的user属性

user属性源码

 @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
    

判断self里面有没有_user这个属性,一开始是没有的,它就会走下面的上下文管理器, self._authenticate()

点_authenticate这个方法,这个方法还在Request对象里面,

_authenticate源码

     def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

这里的for循环的authenticators一定是一个可迭代对象,我们要到Request对象里面去找,

class Request:
   def __init__(self, request, parsers=None, authenticators=None,
                negotiator=None, parser_context=None):
       assert isinstance(request, HttpRequest), (
           'The `request` argument must be an instance of '
           '`django.http.HttpRequest`, not `{}.{}`.'
           .format(request.__class__.__module__, request.__class__.__name__)
       )

       self._request = request

       self.authenticators = authenticators or ()

我们发现是在这个类里面初始化得到的,这个类是dispatch包装request得到的,request = self.initialize_request(request, *args, **kwargs)点击initialize_request这个方法

我们发现了authenticators


        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
 def get_authenticators(self):
      
        return [auth() for auth in self.authentication_classes]

这个是列表生成式,从自己`authentication_classes去取,取出一个加括号执行,这个self是APIView里面的对象的属性,因为自己视图类里面没有

  authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

列表中是一对对象,是视图类中配置的authentication_classes=[类名]对象

所以Request对象中的 authenticators=self.get_authenticators()是列表[类的对象],被传到了Request对象定义的这个 self.authenticators = authenticators or ()传给了authenticators 里面,

_authenticate源码里面的for循环,就是self.authenticators 配置的一堆认证产生类对象组成的list,每次循环拿到一个对象

   def _authenticate(self):
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

authenticator.authenticate这个就是为什么要重写这个方法,执行这个方法,返回的时候是两个值,解压赋值,认证逻辑通过,返回两个值,一个值给了Request对象的user,认证失败,抛出异常APIException,也可以抛出AuthenticationFailed它里面也是继承了APIException

3 认证组件的使用

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 认证逻辑,通过返回两个值
        token = request.GET.get('token') #token = request.META.get('HTTP_TOKEN')这个是放在请求头里面
        if token:
            from app01 import models
            user_token = models.UserToken.objects.filter(token=token).first()
            if user_token:
                return user_token.user,token
            else:
                raise AuthenticationFailed('认证失败')
        else:
            raise AuthenticationFailed('请求地址中需要携带token')

4 全局使用和局部禁用

全局使用在settings里面配置
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
局部使用:
authentication_classes=[MyAuthentication]
局部禁用:在登陆的时候就不需要,因为你还没登录呢,如何做认证
authentication_classes=[] 里面什么都不加就可以了

posted @ 2020-07-09 20:56  小子,你摊上事了  阅读(167)  评论(0编辑  收藏  举报