新的认证方式,由三部分组成:头header,payload(一些用户信息),签名

主要是防篡改,

"""
1)jwt分三段式:头.体.签名 (head.payload.sgin)
2)头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
	"company": "公司信息",
	...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
	"user_id": 1,
	...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
	"head": "头的加密字符串",
	"payload": "体的加密字符串",
	"secret_key": "安全码"
}
"""


校验:根据客户端带token的请求 反解出 user 对象
"""
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且是同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
"""


drf项目的jwt认证开发流程

"""
1)用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2)校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用
"""

两种方式自定制基于jwt的认证类

# 也可以继承drf的BaseAuthentication,要自己写 user=self.authenticate_credentials(payload)的功能
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.utils import jwt_decode_handler
class MyJwtAuth(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value=request.META.get('HTTP_AUTHORIZATION')
        if jwt_value:
            try:
                # jwt_decode_handler可以通过token取出payload,并且有校验功能
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignatureError:
                raise AuthenticationFailed('签名过期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('无效的token')
            except Exception as e:
                raise AuthenticationFailed(str(e))
            # authenticate_credentials方法返回的是用户对象
            user=self.authenticate_credentials(payload)
            return user,jwt_value
        raise AuthenticationFailed('没有携带认证信息')

多方式登录,手动签发token


from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
from rest_framework.exceptions import ValidationError


    def validate(self, attrs):
        user=self._get_user(attrs)

        token=self._get_token(user)
        self.context['token']=token
        # self.context['user']=user
        self.context['username']=user.username
        return attrs

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

        if re.match('1[3-9][0-9]{9}',username):
            user=UserInfo.objects.filter(telephone=username).first()
        elif re.match('.+@.+',username):
            user=UserInfo.objects.filter(email=username).first()
        else:
            user=UserInfo.objects.filter(username=username).first()
        if user:
            res=user.check_password(password)
            if res:
                return user
            else:
                raise ValidationError('密码错误')
        raise ValidationError('用户不存在')

    def _get_token(self,user):
        payload=jwt_payload_handler(user)
        token=jwt_encode_handler(payload)
        return token

配置token过期时间

import datetime

JWT_AUTH = {

# 时间单位可以是:days=7 
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
}