drf框架中的认证组件

05-01 认证组件

认证规则:

  • 没有携带认证信息:返回None --> 游客
  • 携带认证信息,校验失败:抛异常 -->非法用户
  • 携带认证信息,校验成功:返回user对象,token --> 普通用户

全局配置:

# settings.py

REST_FRAMEWORK = {
    DEFAULT_AUTHENTICATIONS_CLASSES = [
        ...
        # 需要认证的方法
    ]
}

局部配置:

# views.py
from rest_framework.views import APIView

class MyAPIView(APIView):
    # 自定义类属性authentication_classes
    # TokenAuthentication:自定义认证规则
    authentication_class = TokenAuthentication 
    ...

自定义认证类:

# api.utils.authentications.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

# 自定义认证类
class TokenAuthentication(BaseAuthentication):
    """
    自定义认证类流程:
    1. 继承BaseAuthentication类,重写authenticate方法
    2. 认证规则见文章开头
    """
    prefix = 'Token'
    
    def authenticate(self, request):
        # 获取前台传过来的token
        auth = request.META.get('HTTP_AUTHORIZATION')
        # 如果没有token就返回None,表明是游客
		if not auth:
            return None
        
        auth_list = auth.split()
        
        if len(auth_list) != 2, or auth_list[0].lower() != self.prefix.lower():
            raise AuthenticationFailed('Token 非法')
        
        token = auth_list[1]
        
        # 校验token,失败抛异常,成功返回(user, token)
        # _get_obj(token)自定义校验规则,后面jwt会有封装好的方法提供
        user = _get_obj(token)
        
        return user, token

自定义校验token方法:

# authentications.py

# 校验token
def _get_obj(token):
    """
    校验token
    1. 拆封token: 一段、 两段、 三段、
    2. 用户名: b64decode(一段)
    3. 用户主键: b64decode(二段)
    4. 碰撞解密: md5(用户名+用户主键+服务器密钥) == 三段
    """
    token_list = token.split('.')

    if len(token_list) != 3:
        raise AuthenticationFailed('token 异常')

    username = json.loads(base64.b64decode(token_list[0])).get('username')
    pk = json.loads(base64.b64decode(token_list[1])).get('pk')

    md5_dic = {
        'username': username,
        'pk': pk,
        'key': settings.SECRET_KEY
    }

    if token_list[2] != hashlib.md5(json.dumps(md5_dic).encode()).hexdigest():
        raise AuthenticationFailed('token 异常')

    user_obj = MyUser.objects.filter(pk=pk, username=username).first()

    return user_obj

对应登陆签发token代码如下:

# serializers.py

# 登陆反序列化类
class LoginModelSerializer(serializers.ModelSerializer):
    """登陆反序列化类"""

    user = serializers.CharField()
    pwd = serializers.CharField()

    class Meta:
        model = models.MyUser
        fields = ['user', 'pwd']

    # 定义全局钩子验证用户密码是否正确
    def validate(self, attrs):
        username = attrs.get('user')
        password = attrs.get('pwd')

        user_obj = auth.authenticate(username=username, password=password)

        if user_obj:
            self.user = user_obj
            self.token = _get_token(user_obj)

        else:
            raise serializers.ValidationError('用户或密码错误')

        return attrs


# 生成token
# 签发算法:b64encode(用户名).b64encode(用户主键).md5(用户名+用户主键+服务器秘钥
def _get_token(obj):
    """生成token"""
    t1 = base64.b64encode(json.dumps({'username': obj.username}).encode()).decode()
    t2 = base64.b64encode(json.dumps({'pk': obj.pk}).encode()).decode()
    t3_json = json.dumps({
        'username': obj.username,
        'pk': obj.pk,
        'key': settings.SECRET_KEY,
    })
    t3 = hashlib.md5(t3_json.encode()).hexdigest()

    return '%s.%s.%s' % (t1, t2, t3)
posted @ 2019-11-26 19:02  17vv  阅读(163)  评论(0编辑  收藏  举报