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)