DRF authority authentication

Posted by piet on Wed, 19 Jan 2022 01:04:44 +0100

1, First of all, understand the process of CBV in Django. The full name of CBV is Class Base View, and its Chinese name is class view. In short, the use method of CBV in Django is class name as_view() before entering as_ In view (), the dispatch() method will be executed first. In Django's view, the dispatch() method only reflects the url and returns the HttpRequest object, while in DRF, the Request object inherits the HttpRequest object;

2, dispatch() method in Django and DRF

(1) Django: dispatch() method of View

def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    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
    return handler(request, *args, **kwargs)

(2) DRF: dispatch() method of APIView

def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    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

(3) Differences and principles

It can be seen that the dispatch() method in DRF just inherits the original Django method and adds something. The main difference is that the final returned request has changed. You can see that DRF initializes the request first

request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

At this time, the request has changed, and a request object is returned, which includes the original request of Django and some other contents; It is not difficult to find that the returned object also includes authentication related content authenticators. What does this object contain? Continue to enter the method:

authenticators=self.get_authenticators(),
def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    return [auth() for auth in self.authentication_classes]

This method returns a list of instantiated objects. For self authentication_ Classes are traversed and instantiated. In fact, when you see this, you can know why you can directly put the class name in the list when referring to the authentication class to realize the function, because authentication will be found in the source code_ Classses = [XXXX, XXXX], for authentication_ The class SES list is instantiated and then returned, and an object with both request and authenticators lists is obtained:

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=[xxxx(), xxxx()],
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

After a request object is returned, the request at this time is request. Next, continue to see the initial() method in the dispatch() method. Enter the method. For the authentication function, you can continue to see that the authentication will be executed inside initial(),

def initial(self, request, *args, **kwargs):
    # In order to facilitate reading, other irrelevant source code has been deleted. You can view the source code yourself if you need to see it
    self.perform_authentication(request)
def perform_authentication(self, request):
    """
    Perform authentication on the incoming request.

    Note that if you override this and simply 'pass', then authentication
    will instead be performed lazily, the first time either
    `request.user` or `request.auth` is accessed.
    """
    request.user

Finally, the source code authentication finds the user attribute of the request. As mentioned earlier, the request here is actually a request object, so go back and find the user attribute in the request object to know what the DRF has executed next

@property
def user(self):
    """
    Returns the user associated with the current request, as authenticated
    by the authentication classes provided to the request.
    """
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
            self._authenticate()
    return self._user
def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    for authenticator in self.authenticators:
        try:
            user_auth_tuple = authenticator.authenticate(self)
        except exceptions.APIException:
            self._not_authenticated()
            raise

Finally_ The authenticate() method traverses the objects in the instantiated object list and returns an ancestor. Therefore, when customizing an authentication class, you need to define an authenticate() method to execute the method of the authentication class;

3, Customize an authentication class

class MyAuthenticate(BaseAuthentication):

    def authenticate(self, request):
        token = request.data['token']
        token_obj = UserToken.objects.filter(token=token).first()
        if not token_obj:
            raise exceptions.AuthenticationFailed('User authentication failed!')
        return (token_obj.user, token_obj)

    def authenticate_header(self, request):
        pass
class APIView(APIView):
    authentication_classes = [MyAuthenticate, ]

 

Topics: Python Django Back-end