03-Django REST framework (03-authentication, privilege, throttling)

Posted by celavi on Fri, 23 Aug 2019 10:15:32 +0200

Articles Catalogue

1. User login authentication

  • a. Some API s require users to log in successfully before they can access them; others can be accessed without logging in.

  • b. Basic Use of Authentication Components
    Solutions:
    a. Create two tables (user table, token table)
    b. User login (return token and save to database)

  • c. Two operations are involved (global authentication, separate authentication)

  • e. Use the built-in authentication class in the framework (inherit from BaseAuthentication when you write your own authentication class, more standardized, do not do it from scratch)

      1. Authentication class, must inherit: from rest_framework.authentication import BaseAuthentication
      1. Other authentication classes: Basic Authentication (an authentication class that allows browsers to automatically generate modeless dialog boxes, Session Authentication, etc.) are based on the Base Authentication browser to help encrypt and pass it in the request head, which is basically unnecessary)
    • 3. In most scenarios, inherit from BaseAuthentication and then define authentication classes themselves
      The basic categories are as follows:
class BaseAuthentication(object):
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):   # Definition of the response header returned when authentication fails
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass

Certification Carding:

1. Use

  • Create a class: inherit BaseAuthentication; Implement: authenticate method, where the authenticate_header method, without writing, directly by the base class pass

  • The return value of authenticate:

    • 1. Return to None, skip and let the next authentication execute.
    • 2. Return (throw) exceptions (receive and process queue exceptions in the upper class here) raise exceptions. Authentication Failed (user authentication failure from rest_framework import exceptions)
    • 3. Return a tuple (element 1, element 2) element 1 is assigned to request.user; element 2 is assigned to request.auth, which can call the two returned in views.py view, such as getting the object user that passed the validation (the user-related information is returned when the function is defined).
  • Local use (locally validated for certain classes, adding static fields for individual classes)

from rest_framework.authentication import BaseAuthentication,BasicAuthentication

class UserInfoView(APIView):
    """
    //Order-related business
    """
    authentication_classes = [BasicAuthentication,]   # Set this static field separately
    def get(self,request,*args,**kwargs):
        print(request.user)
        return HttpResponse('User Information')
  • Global use (configure files in settings):
    • The files used globally are path strings, as follows; when used locally, just add the name of the validation class directly.
    
    

REST_FRAMEWORK = {
# Authentication classes used globally

```python
REST_FRAMEWORK = {
  # Authentication classes used globally
  "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],  #Location of class files written inside
  # "UNAUTHENTICATED_USER":lambda: "Anonymous User"
  "UNAUTHENTICATED_USER":None, # Anonymous, request.user = None
  "UNAUTHENTICATED_TOKEN":None,# Anonymous, request.auth = None
}

2. Source code flow

  • dispatch
    • Encapsulation request
      • Gets the defined authentication class (global/local) and creates the object when the list is generated.
    • initial
      • perform_authentication
        Request. user )
        - user
        - authenticate

2. Authority

class MyPermission(object):

    def has_permission(self,request,view):
        if request.user.user_type != 3:
            return False
        return True
                                

class OrderView(APIView):
    """
    //Order-related services (only SVIP users have permission)
    """
    permission_classes = [MyPermission,]
    
    def get(self,request,*args,**kwargs):
        # request.user
        # request.auth
        self.dispatch
        ret = {'code':1000,'msg':None,'data':None}
        try:
            ret['data'] = ORDER_DICT
        except Exception as e:
            pass
        return JsonResponse(ret)

Permission knowledge combing:
Ibid., for code specification, it's best to inherit from BasePermission

  • Class, must inherit: BasePermission, must implement: has_permission method, which also has_object_persion method to refine the object-specific permission judgment
    from rest_framework.permissions import BasePermission

    class SVIPPermission(BasePermission):
        message = "Must be SVIP Accessibility"
        def has_permission(self,request,view):
            if request.user.user_type != 3:
                return False
            return True
  • Return value:
    • True, with access
    • False, no access
  • Local use
    class UserInfoView(APIView):
        """
        Order-related business (ordinary users, VIP)
        """
        permission_classes = [MyPermission1, ]

        def get(self,request,*args,**kwargs):
            return HttpResponse('User Information')
  • Global use
    REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission']
    }

3. Access Frequency Control (Throttle/Limit)

Control the access frequency of requests and implement it concretely

Code implementation is to take IP to record, for anonymous users who are not logged in, we can only get ip, if the IP access is changed, this can not be controlled.

Access to the ip address: request.META.get('REMOTE_ADDR)

  • 1. Class, if inheritance: BaseThrottle, to achieve their own: allow_request, wait
  • 2. Class, if inherited: SimpleRateThrottle, only need to implement: get_cache_key, one of the fields: scope = "Luffy" (key that restricts throttling frequency in configuration file)
import time
VISIT_RECORD = {}

class VisitThrottle(object):
    """60s Only three visits within"""

    def __init__(self):
        self.history = None

    def allow_request(self,request,view):
        # 1. Getting User IP
        remote_addr = request.META.get('REMOTE_ADDR')
        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True
        history = VISIT_RECORD.get(remote_addr)
        self.history = history

        while history and history[-1] < ctime - 60:
            history.pop()

        if len(history) < 3:
            history.insert(0,ctime)
            return True

        # return True    # Represents continued access
        # return False # Represents that access is too frequent and restricted

    def wait(self):
        """
        //How many seconds do you need to wait for access?
        :return:
        """
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

      
class AuthView(APIView):
    """
    //Used for user login authentication
    """
    authentication_classes = []
    permission_classes = []
    throttle_classes = [VisitThrottle,]

    def post(self,request,*args,**kwargs):
        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = "ERROR Incorrect username or password"
            # Create token for logged-in users
            token = md5(user)
            # Existence updates and nonexistence creates
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = 'Request exception'

        return JsonResponse(ret)

Source code flow combing

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

# get_ident() in the code can be identified not only by remote IP, or by self-written identity, but also by the Ip of the transmitted agent. But the premise is that the IP data must be included in the request, otherwise it can not be realized.

class MyThrottle(SimpleRateThrottle):
    """Restrict all users"""
    scope = 'Luffy'
    def get_cache_key(self, request, view):
    #Set the unique identifier for frequency limitation. Cached keys, you can set the remote ip as the identity, using get_ident()
        return self.get_ident(request)


class UserThrottle(SimpleRateThrottle):
    """Restrict access frequency of logged-in users"""
    scope = 'LuffyUser'
    def get_cache_key(self, request, view):
        #Set a unique identifier for frequency restriction. Cached key, remote ip can be set as identity, using get_ident()
        return request.user.username
  • Global use
            REST_FRAMEWORK = {
REST_FRAMEWORK = {
    # Authentication classes used globally
    "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ],
    # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ],
    # "UNAUTHENTICATED_USER":lambda: "Anonymous User"
    "UNAUTHENTICATED_USER":None, # Anonymous, request.user = None
    "UNAUTHENTICATED_TOKEN":None,# Anonymous, request.auth = None
    "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'],
    # The settings here are for all user frequency limitations, and for individual settings for logged-in users, they can be auth-auth-authenticated directly.
    # Add fields directly; if the frequency is the same, global restrictions can be imposed directly on ip.
    "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
    "DEFAULT_THROTTLE_RATES":{
        "Luffy":'3/m',   # Three times per minute (SimpleRateThrottle internal method implementation)
        "LuffyUser":'10/m',    #
    }
}
  • Use it locally and add the guide function directly to line throttle_classes = [UserThrottle,] (see the code above)

Topics: Lambda Database Session Permission denied