Certification authority, frequency, and more custom log issued token-

Three certification flow chart

Routing Configuration

Under the new document in the applicationrouter.py

# router.py
from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter


class SimpleRouter(DRFSimpleRouter):
    routes = [
        # List route.
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create',
                'put': 'multiply_update',
                'patch': 'multiply_partial_update',
                'delete': 'multiply_destroy'
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]


router = SimpleRouter()

urls.py

# urls.py

from django.conf.urls import url, include
from .router import router
from . import views
router.register('users', views.UserListAPIViewSet, basename='user')

urlpatterns = [
    url(r'', include(router.urls))
]

Certified Components

Configuring drf-jwtlocal certification class certification component framework can only be decided request.user, not the authority to determine, it is generally configure global

# settings.py

REST_FRAMEWORK = {
    # 认证组件
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ],

}

New File authentications.py, custom certification

from rest_framework.authentication import BaseAuthentication

# 自定义认证类
# 如果使用session认证,drf默认提供了SessionAuthentication
# 如果使用drf-jwt认证,drf-jwt默认提供了JSONWebTokenAuthentication
# 如果自定义签发与校验token,才需要将校验token的算法封装到自定义的认证类

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        pass
        # 1、从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)
        #       如果设置了反爬
        # 2、没有token,返回None,代表游客
        # 3、有token,进入校验    不通过,抛异常,代表非法用户;通过,返回(user, token),代表合法用户

Custom authentication rules:

  • Reception in the request header to get submitted token (typically from the HTTP_AUTHORIZATION take, may be agreed upon with the front)

    • If the anti-climb and other measures set up, check what anti-climb (first token)
  • No token, return None, on behalf of tourists
  • There token, enter verification
    • Not by throwing an exception, on behalf of illegal users
    • By return (user, token), on behalf of legitimate users

Authentication component source code analysis:

Permissions components

Permissions class is to achieve BasePermission class, override has_permission method, if you have permission to return True, no authority returns False.

Local configure permissions:

# views.py

# 权限组件
permission_classes = [permissions.MyPermission]

Custom permission class

# permissions.py
from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    def has_permission(self, request, view):
        """
        根据需求,request和view的辅助,制定权限规则判断条件
        如果条件通过,返回True
        如果条件不通过,返回False
        """
        pass

Drf class configuration comes with permission

  • drf default provides some kind permission
    • AllowAny: tourists and logged-on user full authority
    • IsAuthenticated: Only logged-on user has full authority
    • IsAdminUser: Only the background user (admin) have full authority
    • IsAuthenticatedOrReadOnly: Visitors have read access, log the user has full authority
  • If you have special needs, need custom permission class
    • Such as: There are only superuser privileges, only vip users have permission, only certain ip network user has permission only to a privileged view and its subclasses
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from .response import APIResponse
from rest_framework.permissions import IsAuthenticated

class UserListAPIViewSet(mixins.ListModelMixin, GenericViewSet):
    permission_classes = [IsAuthenticated]
    pass

drf-jwt issued token source code analysis

"""
drf-jwt 签发token
1、username、password 通过auth组件的authenticate方法得到user对象
2、user对象、通过drf-jwt框架的jwt_payload_handler函数包装 payload载荷
3、payload载荷 通过drf-jwt框架的jwt_encode_handler函数签发 token字符串

注:可以借助jwt_payload_handler和jwt_encode_handler 两个函数完成自定义token的签发
"""

Multi-issued token to log in

  • token issued only by the login interfaces

  • Login interface is also APIView subclass will be verified using authentication, permissions, component

  • Conclusion: Regardless of the system default, or global settings configuration is what authentication and rights component, login interfaces do not participate in any verification and certification authority. So, be sure to login interfaces locally disable authentication and permissions .

from rest_framework.views import APIView


class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        serializers = serializer.LoginModelSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username': '',
            'token': ''
        })

这样会有个问题:post方法默认走的是数据库的增方法

默认的校验规则里面是走的数据库,我们可以自定义字段

# 序列化类 serializer.py
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField(min_length=3, max_length=16)
    password = serializers.CharField(min_length=3, max_length=16)
    class Meta:
        model = models.User
        fields = ['username', 'password']

    # 用全局钩子,完成token的签发
    def validate(self, attrs):
        # 1、通过username、password完成多方式登录校验,得到user对象
        user = self._validate_user(attrs)
        # 2、user对象包装怕payload载荷
        payload = jwt_payload_handler(user)
        # 3、payload载荷签发token
        token = jwt_encode_handler(payload)
        # 4、将user与token存储到serializer对象中,方便在视图类中使用
        self.content = {
            'user': user,
            'token': token
        }
        return attrs


    def _validate_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')

        if re.match(r'.*@.*', username):    # 邮箱
            user = models.User.objects.filter(email=username).first()
        elif re.match(r'^1[1-9][0-9]{9}$', username):  # 电话
            user = models.User.objects.filter(mobile=username).first()
        else:  # 用户名
            user = models.User.objects.filter(username=username).first()

        if not user or not user.check_password(password):
            raise serializers.ValidationError({'message': '用户信息异常'})

        return user
# views.py

from rest_framework.views import APIView


class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        serializers = serializer.LoginModelSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        print(serializers.content)
        return APIResponse(results={
            'username': serializers.content.get('user').username,
            'token': serializers.content.get('token')
        })

总结:认证与权限绑定使用

  • 每一个视图类都要进行认证,且认证规则一致,多余全局配置认证即可
  • 每一个视图类都要进行权限校验,默认配置的是 不限制(AllowAny),但实际开发中,视图类的访问权限不完全相同,所以要在具体的视图类,配置具体的权限规则

VIP用户认证权限例子

# views.py

from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
    # 权限:只要VIP用户可以查看 个人详细信息
    permission_classes = [permissions.VIPUserPermission]
    def retrieve(self, request, *args, **kwargs):
        return APIResponse(results={
            'username': request.user.username,
            'email': request.user.username,
            'mobile': request.user.username,
            'data_joined': request.user.date_joined,
        })
# permission.py
from rest_framework.permissions import BasePermission

# VIP用户权限
class VIPUserPermission(BasePermission):
    def has_permission(self, request, view):
        for group in request.user.groups.all():
            if group.name.lower() == 'vip':
                return True
        return False

频率组件

# views.py
class UserListAPIViewSet(mixins.ListModelMixin, GenericViewSet):
    from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
    # 频率组件
    # throttle_classes = [AnonRateThrottle]
    throttle_classes = [UserRateThrottle]
    
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializer.UserModelSerializer

    def update(self, request, *args, **kwargs):
        return APIResponse()

    def multiply_update(self, request, *args, **kwargs):
        return APIResponse(msg='群改')

在settings.py配置文件中配置

REST_FRAMEWORK = {
    # 频率组件,频率类一般做局部配置,但是频率调节在settings中配置
    'DEFAULT_THROTTLE_RATES': {
        'user': '5/min',   # 登录用户限制
        'anon': '3/min',   # 匿名用户限制
    },

}

自定义频率类

自定义频率类:

  • drf默认提供了一些频率类
    • AnonRateThrottle:只对游客进行频率限制
    • UserRateThrottle:对所有用户进行频率限制
  • 如果由特殊需要,需要自定义频率类
    • 如:对ip进行限次、对电话进行限次、对视图某些信息进行限次

方法步骤:

  • 设置scope字符串类属性,同时在settings中进行drf配置DEFAULT_THROTTLE_RATES
  • 重写get_catch_key方法
    • 返回与限制条件有关的字符串,表示限制
    • 返回None,表示不限制
# throttles.py

from rest_framework.throttling import SimpleRateThrottle

class MobileReateThrottle(SimpleRateThrottle):

    scope = 'mobile'
    def get_cache_key(self, request, view):
        if not request.user.is_authenticated or not request.user.mobile:
            return None   # 匿名用户   没有电话号的用户都不限制

        return self.cache_format % {
            'scope': self.scope,
            'ident': request.user.mobile
        }

Guess you like

Origin www.cnblogs.com/setcreed/p/12142649.html