1.2 三大认证

mapping={
    'get': 'list',  # 群查
    'post': 'create',  # 单增、群增
    'put': 'multiple_update',  # 群整改
    'patch': 'multiple_partial_update',  # 群局改
    'delete': 'multiple_destroy',  # 群删
},
mapping={
    'get': 'retrieve',  # 单查
    'put': 'update',  # 单整改
    'patch': 'partial_update',  # 单局改
    'delete': 'destroy'  # 单删
            },

RetrieveAPIView (mixins.RetrieveModelMixin, GenericAPIView) 视图工具类,继承工具视图类的mixins.RetrieveModelMixin,实现单查

ListAPIView (mixins.ListModelMixin, GenericAPIView) 群查

RetrieveUpdateDestroyAPIView (mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView) 单查、单整体改、单局部改、单删

视图集:

ViewSetMixin类

GenericViewSet 继承自ViewSetMixin, GenericAPIView,

ReadOnlyModelViewSet,ModelViewSet两个视图集子类继承自GenericViewSet,就是做了一堆mixin与GenericViewSet相结合,自己在urls文件中配置as_view设置映射关系

三大认证

  • authentication 认证

  • permission 权限

  • throttle 频率

# -------------------- 三大认证 --------------------
    """
    # 认证
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],

    # 权限
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],

    # 频率
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    'DEFAULT_THROTTLE_CLASSES': [],
    """

    # 认证组件
    # 配置自定义认证类(需求小)
    # authentication_classes = [authentications.MyAuthentication]
    # 配置drf-jwt框架的认证类(需求大) => 配置还可以在全局(认证组件只能决定request.user,不是断定权限的地方,所以一般配置全局)
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    # authentication_classes = [JSONWebTokenAuthentication]

    # 权限组件
    # 配置自定义权限类(有需求)
    # permission_classes = [permissions.MyPermission]
    # 配置drf自带的权限类(有需求)
    from rest_framework.permissions import IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
    # permission_classes = [IsAuthenticated]
    # permission_classes = [IsAdminUser]

    # 频率组件
    # 配置drf自带的频率类(有需求)
    from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
    # throttle_classes = [UserRateThrottle]
    # throttle_classes = [AnonRateThrottle]
    # 配置自定义的频率类(需求大)
  • 自定义认证类
    • 如果使用session认证,drf默认提供了SessionAuthentication
    • 如果使用drf-jwt认证框架,drf-jwt框架提供了JSONWebTokenAuthentication
    • 如果是自定义签发与校验token,才需要将校验token的算法封装到自定义的认证类中
    • 自定义authenticate方法的过程:
      • 从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)--如果设置了反爬等措施,校验一下反爬(头token)
      • 没有token,返回None,代表游客
      • 有token,进入校验--不通过:抛AuthenticationFailed异常,代表非法用户;通过:返回(user,token),代表合法用户
  • 自定义权限类
    • drf默认提供了一些权限类
      • AllowAny:游客和登录用户都有全权限
      • IsAuthenticated:只有登录用户有全权限
      • IsAdminUser:只有后台用户(admin用户)有全权限
      • IsAuthenticatedOrReadOnly:游客有读权限,登录用户有全权限
    • 如果有特殊需要,需要自定义权限类
      • 如:只有superuser有权限、只有vip用户有权限、只有某ip网段用户有权限、只有某个视图及其子类有权限
    • 自定义has_permission权限方法的过程:
      • 根据需求,request和view的辅助,制定权限规则判断条件
      • 如果条件通过,返回True
      • 如果条件不通过,返回False
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
    def has_permission(self, request, view):
        print(request.user, request.auth)   #auth就是token,由HTTP_AUTHORIZATION带过来
        return False
#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
  • 自定义频率类
    • drf默认提供了一些频率类
      • AnonRateThrottle:只对游客进行频率限制
      • UserRateThrottle:对所有用户进行频率限制
    • 如果有特殊需要,需要自定义频率类
      • 如:对ip进行限次、对电话进行限制、对视图某些信息进行限次
    • 自定义频率类的过程:
      • 设置scope字符串类属性,同时在settings中进行drf配置
      • 重写get_catch_key方法:返回与限制条件有关的字符串,表示限制;返回None,表示不限制
#settings.py
# drf框架自定义配置
REST_FRAMEWORK = {
    # 异常模块:异常处理函数
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',

    # 认证组件
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # 'rest_framework.authentication.SessionAuthentication',
        # 'rest_framework.authentication.BasicAuthentication'
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ],

    # 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
    'DEFAULT_THROTTLE_RATES': {
        'user': '5/min',
        'anon': '3/min',
        'mobile': '1/min'
    },
}

# drf-jwt自定义配置
import datetime

JWT_AUTH = {
    # 过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
    # 是否允许刷新
    'JWT_ALLOW_REFRESH': False,
    # 最大刷新的过期时间
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
from rest_framework.throttling import SimpleRateThrottle

class MobileRateThrottle(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 %{     
            #%s无名占位  
            #%(scope)s有名占位,后面跟字典 eg:%(scope)s % {'scope':123}
            'scope':self.scope,     #
            'ident':request.user.mobile   #不同匿名用户的唯一标识
        }
  • drf-jwt签发token源码分析
    • 内部在全局钩子中完成token的签发
    • 用auth组件的authenticate方法进行校验,该方法也只能校验username和password,得到用户user
    • 如果是活跃用户用jwt_payload_handler方法将用户包装成载荷payload(不是活跃的抛异常)
    • 用jwt_encode_handler方法签发token字符串
    • 将token和user存放在serializer对象中,在外界用request.object.get('token') |get('user')获取
    def validate(self, attrs):
        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }

        if all(credentials.values()):
            user = authenticate(**credentials)

            if user:
                if not user.is_active:
                    msg = _('User account is disabled.')
                    raise serializers.ValidationError(msg)

                payload = jwt_payload_handler(user)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': user
                }
            else:
                msg = _('Unable to log in with provided credentials.')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "{username_field}" and "password".')
            msg = msg.format(username_field=self.username_field)
            raise serializers.ValidationError(msg)
  • 多方式登录(重点)
    • token只能由登录接口 签发
    • 登录接口也是APIView的子类,使用一定会进行 认证、权限 组件的校验
    • 结论:不管系统默认、或是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验,所以登录接口一定要进行认证与权限的局部禁用
from rest_framework.views import APIView
class LoginAPIView(APIView):
    
    authentication_classes = []
    pagination_class = []

    def post(self, request, *args, **kwargs):
        serializer = serializers.LoginModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username': serializer.content.get('user').username,
            'token': serializer.content.get('token')
        })
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
    # post请求,序列化默认当做create动作进行校验,需要校验数据库,create动作username会抛用户已存在异常
    # 抛用户已存在异常是多余的,所以自定义系统校验规则即可
    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()  # type: models.User
        elif re.match(r'^1[3-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

游客也存在request.user中,是匿名用户

猜你喜欢

转载自www.cnblogs.com/lidandanaa/p/12142260.html
1.2
今日推荐