一、自动签发

urls

from rest_framework_jwt.views import obtain_jwt_token
# 使用jwt自带的登录视图
urlpatterns = [
    path('login/', obtain_jwt_token),
]

settings

import datetime
JWT_AUTH={
    # 配置响应格式,必须和自动签发使用
  'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.my_jwt_response_payload_handler',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}

utils

# 重写jwt响应格式(需要到settings配置)
# 与之配合使用的必须是自动签发
def my_jwt_response_payload_handler(token, user=None, request=None): # 返回什么,前端就能看到什么样子
    return {
        'token': token,
        'msg':'登录成功',
        'status':100,
        'username':user.username

    }

然后直接在前端提交post请求发送账号和密码,会返回我们定义好的响应格式

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imh6IiwiZXhwIjoxNTk1NDE1MTEyLCJlbWFpbCI6IiJ9.BxBdsm6oBz8iPAwSSpo_7IaU4pBp6RjK4c0GJ_FYN1E",
    "msg": "登录成功",
    "status": 100,
    "username": "hz"
}

然后拿出token对测试类发送测试请求

class TestAPI(APIView):
    def get(self,request):
        print(request.user)
        return Response("ok")
# 因为内置的他没有对匿名用户设置拦截,素以匿名用户也能看到ok
# 我们用request.user来区分
# 这里可能会出现我登录了很多次,用每次不同的token都能登录
# 这是因为token校验的是规则,是要加密规则符合且没有超时,那用哪次token都一样的

二、手动签发

utils

from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication,jwt_decode_handler
import jwt
from rest_framework.exceptions import AuthenticationFailed
class MyAuthentication(BaseJSONWebTokenAuthentication):
    # 这里重写的逻辑和BaseJSONWebTokenAuthentication里的authenticate一模一样
    def authenticate(self, request):
        jwt_token = request.META.get('HTTP_AUTHORIZATION') # 获取浏览器传来的token
        if jwt_token:
            try:
                payload = jwt_decode_handler(jwt_token) # 传入token,拿出第二段用户信息,有内置的校验token功能
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('用户非法')
            except Exception as e:
                # 所有异常都会走到这
                raise AuthenticationFailed(str(e))
            # 通过内置的方法把payload转换成用户对象
            user = self.authenticate_credentials(payload)
            return user,None # ===》request.user,request.auth
        raise AuthenticationFailed('您没有携带认证信息')

sers

from rest_framework import serializers


# 多方序列化校验登录
import re
from rest_framework.exceptions import ValidationError
from app01 import models
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
class LoginSer(serializers.ModelSerializer):
    # 我们要提交校验数据的时候,如果直接用下面Meta绑定给模型类的话
    # 关键点2:这里如果不写username的话,序列化器直接用的是模型类的username
    # 这两者的区别在于,如果覆盖写了username,他表示的可以是任何前端传来的数据,如果是模型类绑定,那只能是用户名了
    # 我们这里username用于多方登录的校验数据,必须要重写
    # 而password不用重写,因为password用的就是模型类本身的
    username = serializers.CharField()
    class Meta:
        model = models.User
        fields =['username','password']
    def validate(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        if username:
            if re.match('^1[3-9][0-9]{9}$', username):
                user = models.User.objects.filter(mobile=username).first()
            elif re.match('^.+@.+$', username):  # 邮箱
                user = models.User.objects.filter(email=username).first()
            else:
                user = models.User.objects.filter(username=username).first()
            if user:
                if user.check_password(password):
                    # 关键点3:jwt_payload_handler把用户数据对象转化成用户信息的字典
                    # jwt_encode_handler把用户信息的字典转化成token
                    payload = jwt_payload_handler(user)
                    # print('user:',user,type(user))
                    token = jwt_encode_handler(payload)
                    # print('payload:',payload,type(payload))
                    # print('token:',token)
                    # 关键点4:如果我们要给序列化器添加数据,让视图函数去使用
                    # 通常都是传给对象的context属性,当然直接赋值也可以,这只是他给我们提供的传值接口
                    self.context['token'] = token
                    self.context['user'] = user
                    self.user = user
                    return attrs
                else:
                    raise ValidationError('密码错误')
            raise ValidationError('不存在用户')
        raise ValidationError('请输入用户名')


views

class LoginApi(ViewSet):
    authentication_classes = []
    def login(self, request):
        # 在调用序列化类给context传数据,可以直接在序列化类中调用
        # 关键点1:注意区分序列化传值与反序列化
        # 这里只要拿字典取校验数据,那就传给data
        # 如果是要把数据对象转化成字典就传给instance
        user_ser = sers.LoginSer(data=request.data, context={'request': request})
        user_ser.is_valid(raise_exception=True)
        token = user_ser.context.get('token')
        user = user_ser.context.get('user')
        print(user_ser.user)
        return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})
07-16 03:43