drf—jwt认证

1、jwt认证

1.1 jwt认证原理

1、用户第一次登陆时,登陆成功后,服务器通过token签发算法,生成token字符串并发送给客户端

2、客户端登陆成功后,收到响应的token,保存在cookie中

3、客户端再发起非登陆请求,将token携带发送给服务器

4、服务器接收请求,判断为非登陆请求,开始调用token校验算法校验token,校验通过即正常处理请求,否则拒绝请求


优点

1、不用在数据库存储用户登陆状态,减少数据库的写操作,大大提高性能

2、校验token时,在token字符串正确情况下,才会查询数据库,校验用户,只有数据库的读操作,可以异步进行

3、校验算法,可以非常容易的实现服务器的同步,即利于负载均衡架构的服务端


1.2 drf—jwt签发算法:

1、头字符串

  • 数据来源:包含一些版本信息、公司名称等公开信息的json字典
  • 算法:使用base64,根据json字典生成一个json字符串

2、载荷字符串

  • 数据来源:包含用户名、设备id、登陆时间等客户端状态的隐秘信息的json字典
  • 算法:使用base64,根据json字典生成一个json字符串

3、签名字符串

  • 数据来源:头字符串,载荷字符串
  • 不可逆的hs256算法,根据前两个字符串再加服务端的唯一安全码,生成一个随机字符串

由这三个字符串,组成token字符串


jwt校验算法:

1、服务端拿到三段式token

2、按.切分字符串,获取头、载荷、签名字符串

3、由头、载荷字符串和服务端的唯一安全码,按照签名算法生成签名字符串

4、比较生成的字符串与客户端发来的签名字符串

5、校验通过,解析载荷字符串,校验用户信息(是否存在该用户)

5、载荷校验通过,即token校验通过


2、配置使用jwt

1、下载jwt第三方库

在github上搜索jwt即可,选取python的rest_framework版本即可


2、配置

跟drf的认证组件的配置相同

1、全局配置:

# 一般配置为全局,因为除了登陆请求,都需要认证token
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ]

2、局部配置:

# 一般不配置为局部,只在登陆视图中,通过局部配置的方式,禁用token的认证

# 局部禁用认证
authentication_classes = []  

# 局部启用配置,需要先导入要配置的类,此处以jwt为例
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
authentication_classes = [JSONWebTokenAuthentication]

3、示例:多方式登陆认证

# view.py
class LoginAPIView(APIView):
    '''
    1   token只能由登陆接口 签发
    2   登陆接口也是APIView的子类,一定会进行 认证、权限组件的校验
    3   但是登陆接口不能参与任何认证或者权限的校验,无论时默认配置、全局配置或者局部配置
    结论:在登陆视图中,局部禁用认证组件和权限组件
    '''
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        serializer = serializers.LoginModelSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return response.APIResponse(results={'username':serializer.data.get("username"),'token':serializer.content.get('token')})

    
# 认证过程,在序列化过程中进行
# serializers.py
class LoginModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField(max_length=16, min_length=3)
    password = serializers.CharField(max_length=16, min_length=3)

    class Meta:
        model = models.User
        fields = ['username', 'password']

    def validate(self, attrs):
        user = self._validated_user(attrs)
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        print(token)
        self.content = {'user':user,
                        'token':token}
        return attrs

    def _validated_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
		
        # 使用正则匹配username,从而区分登陆方式
        if re.match('.*@.*', username):  # 邮箱
            user = models.User.objects.filter(email=username).first()
        elif re.match('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

posted @ 2020-01-06 19:36  W文敏W  阅读(272)  评论(0编辑  收藏  举报