drf三大认证和多方式登录

认证组件

jwt只负责认证和签发token,对于权限、频率的校验还是走但drf的permmsion和throtting或自定义的校验规则

认证可以配置在局部、全局,但是因为用户登录后所访问的接口都要携带token 进行验证,所以配在全局比较合适,不然我们所写的视图类要一一配置,很麻烦;而权限的校验,根据用户的角色不同,对某接口访问的权限也不同,所以权限的配置只能在具体的视图类中,限制只能某一类角色能访问。

# *局部配置drf-jwt框架的认证类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
 1) authentication_classes = [JSONWebTokenAuthentication] # 视图类定义我们的认证类属性
 # * settings文件全局配置drf-jwt框架的认证类
 2) REST_FRAMEWORK = {
    # 认证组件,走jwt的认证类
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ],
}
# 如何自定义认证类、以及认证类规则?
'''
1)自定义类继承BaseAuthentication
	2)重写authenticate方法
	3)方法体(认证规则):
		- 从请求头HTTP_AUTHORIZATION中拿token
		- 没有token,返回None,游客(匿名用户)
		- 有token,解析异常,抛异常AuthenticationFaild,非法用户  #  前台收到Unauthorized 401
		- 有token,解析正常,返回(user, token),合法用户
	使用:全局配置drf-jwt框架的认证类
'''
	# 局部配置自定义的认证类,一般在全局settings中配置:
    1) authentication_classes = [authentications.MyAuthentication, ] # 视图类定义我们的认证类属性
    # * settings文件全局配置自定义的认证类
	2) REST_FRAMEWORK = {
    # 认证组件,走自定义的认证类
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'api.authentication.MyAuthentication'
    ],
}

权限组件


# 视图类局部配置drf自带或自定义权限类
class MyAPIView(APIView):
	permission_classes = [permissions.VIPUserPermission]
	
'''
1.drf自带权限类:IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
2.自定义权限类
    1)自定义类继承BasePermission
        2)重写has_permission方法
        3)方法体(权限规则):
            - 根据需求制定判断条件
            - 返回True时,代表有权限
            - 返回False,代表无权限
        使用:局部配置 drf提供的 或 自定义的
'''
# permission.py
class VIPUserPermission(BasePermission):  # 只有vip分组用户有权限
    def has_permission(self, request, view):
        for group in request.user.groups.all():
            if group.name.lower() == 'vip':
                return True  # 有权限
        return False  # 无权限

频率组件


"""
系统频率类
1) drf默认提供了一些频率类 
    AnonRateThrottle:只对游客进行频率限制
    UserRateThrottle:对所有用户进行频率限制
2)如果有特殊需要,需要自定义频率类
    如:对ip进行限次、对电话进行限制、对视图某些信息进行限次
"""
* 视图类局部配置drf自带或自定义频率类
class MyAPIView(APIView):
	throttle_classes = [throttles.MobileRateThrottle]
    
# throttles.py 自定义频率类
from rest_framework.throttling import SimpleRateThrottle
class MobileRateThrottle(SimpleRateThrottle):
    """
    1)1)自定义类继承SimpleThrottle
    2)设置scope字符串类属性,同时在settings配置文件配置scope的字符串值对应的频率限制条件
        REST_FRAMEWORK = {
    # 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
    'DEFAULT_THROTTLE_RATES': {
        'user': '5/min',
        'anon': '3/min',
        'mobile': '1/min'
    },
  }
    3)重写get_catch_key方法:
        返回与限制条件有关的字符串,表示限制(要根据不同的用户,动态变化的)
        返回None,表示不限制(某些条件下,不要进行频率限制)
    """
    scope = 'mobile'
    # rate = '3/min'
    def get_cache_key(self, request, view):  # 继承SimpleRateThrottle类,重写get_cache_key方法
        if not request.user.is_authenticated or not request.user.mobile:
            return None  # 匿名用户 或 没有电话号的用户 都不限制

        # 只要有电话号的用户踩进行限制
        return self.cache_format % {
            'scope': self.scope,
            'ident': request.user.mobile
        }  

多方式登录并签发token

'''
# 多方式登录
	1)post请求走序列化类,默认当做create动作处理,所以系统字段默认会校验数据库,可能会出现 数据已存在 异常,所以要摒弃数据库的校验,需要自定义反序列化字段(其实就是去掉数据库校验而已)
	username = serializers.CharField(...)
	2)多个数据整体操作,不管是校验,还是类似于签发token等逻辑,都可以在全局校验钩子中完成
	3)多方式登录体现在 请求的账号类型可能是用户名、邮箱或手机等,采用不同字段校验数据库即可
'''


# views.py
from rest_framework.views import APIView
from .response import  APIResponse
from . import serializers
class LoginAPIView(APIView):
    '''
    1) token只能由登录接口签发
    2) 登录接口也是APIView的子类,使用一定会进行认证、权限、频率组件的校验
    结论:不管系统默认,还是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验
    所以,登录接口一定要进行认证与权限的局部禁用
    '''
    authentication_classes = []
    permission_classes = []
    def post(self,request,*args, **kwargs):

        request_data = request.data
        serializer = serializers.LoginModelSerializer(data=request_data)
        serializer.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username':serializer.content.get('username'),
            'token':serializer.content.get('token')
        })
    
# serializers.py

from rest_framework import serializers
from .import models
class UserModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.User

        fields = ('username','email','phone')

from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
    # post请求,序列化默认当作create动作去校验,系统会去校验数据库该字段的的规则,发现该用户名已经存在会抛异常;
    # 抛用户存在异常是多余的,所以自定义系统校验规则即可
    username = serializers.CharField(max_length=64, min_length=3)
    password = serializers.CharField(max_length=20, min_length=3)
    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 = {
            'username': user.username,
            'token': token
        }
        return attrs

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

        if re.match(r'.*@*.com',username):# 邮箱
            user = models.User.objects.filter(email=username).first()
        elif re.match(r'1[3-9][0-9]{9}',username):# 电话
            user = models.User.objects.filter(phone=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
posted on 2020-05-19 14:34  jueyuanfengsheng  阅读(663)  评论(0编辑  收藏  举报