drf_jwt

JWT:

1、组成: header.payload.signature 头.载荷.签名 2、示例: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im93ZW4iLCJleHAiOjE1NTgzMDM1NDR9.4j5QypLwufjpqoScwUB9LYiuhYcTw1y4dPrvnv7DUyo 3:介绍: header:一般存放如何处理token的方式:加密的算法、是否有签名等 payload:数据的主体部分:用户信息、发行者、过期时间等 signature:签名:将header、payload再结合密码盐整体处理一下
工作原理:

1) jwt = base64(头部).base64(载荷).hash256(base64(头部).base(载荷).密钥)
2) base64是可逆的算法、hash256是不可逆的算法
3) 密钥是固定的字符串,保存在服务器
官网:https://github.com/jpadilla/django-rest-framework-jwt

安装:pip install djangorestframework-jwt

使用:一般是搭配xadmin去实现后台管理

获得token:

在user/url.py里:


from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),
]


利用postman测试发射post请求:


接口:http://127.0.0.1:8000/user/login/

数据:
{
    "username":"admin",
    "password":"admin"
}
可能存在一些问题:

Unable to log in with provided credentials


原因是这种算法内部是需要使用auth_user表去获取用户的账号和密码数据的,而我们没有创建相关的数据,因此需要创建一些数据:

 python manage.py createsuperuser

再次尝试:得到token值:
 "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXh
wIjoxNTY5MjAyODc1LCJlbWFpbCI6IjEzMDYzNDczMzgwQDE2My5jb20ifQ.JgbSItmx0sa4JHuA5I4J58gcoeCgTLKZaPj0T-f5OwI"


虽说拿到了后端生成的token值,可怎么让其真正的实现认证功能呢:

 在settinngs.py里配置jwt的相关数据

import datetime
JWT_AUTH = {
    # 过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
    # 自定义认证类
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',  
}
 序列化组件 user/serializers.py

from rest_framework import serializers
from .models import User
class UserModelSerializer(serializers.ModelSerializer):
    """轮播图序列化器"""
    class Meta:
        model = User
        fields = ["username", "mobile"]


需要注意的是:User这张表继承的是  from django.contrib.auth.models import AbstractUser  ,因为jwt内部走的是auth_user这张表。    同时还需要在配置里将 AUTH_USER_MODEL = 'app的名字.User' 添加上,表示重写了auth的认证表
同时:因为重写了auth表,需要删除一些迁移记录文件:django/contrib/admin/migrations
django/contrib/auth/migrations
xadmin/migrations  如果使用xadmin作为后台管理使用
reversion/migrations 同上
user/migrations
在user/models.py下:


from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser

class  User(AbstractUser):
    mobile = models.CharField(max_length=32,default=0)
    age = models.IntegerField(default=0)
自定义response的返回结果: user/utils.py

from .serializers import UserModelSerializers
def jwt_response_payload_handler(token, user=None, request=None):
    return {
        'token': token,
        # 拿到的是一个序列化后的对象,多个对象加上many=True
        'user': UserModelSerializer(user).data
    }
    # restful 规范
    # return {
    #     'status': 0,
    #     'msg': 'OK',
    #     'data': {
    #         'token': token,
    #         'username': user.username
    #     }
    # }

上述操作完成了jwt的自定义返回值,现在需要实现的是jwt全局认证,基于drf的认证组件实现

import jwt
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication


class JSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        # 下面的 token获取,可以直接 request.META.get()去获取
        token = get_authorization_header(request)
        # 可以在此处 对请求头的字段做出一些限制,实现反扒
        if not token:
            raise AuthenticationFailed('Authorization字段是必须的')

        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignature:
            raise AuthenticationFailed('签名过期')
        except jwt.DecodeError:
            raise AuthenticationFailed('错误的解码签名')
        except jwt.InvalidTokenError:
            raise AuthenticationFailed('非法用户')

        user = self.authenticate_credentials(payload)

        return (user, token)

当然了,需要在settings.py里完成相关配置:全局启用

EST_FRAMEWORK = {
    # 认证模块
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'user.authentications.JSONWebTokenAuthentication',
    ),
}
需要知道,drf_jwt是基于drf框架实现的
局部启用禁用:任何一个cbv类首行


# 局部禁用
authentication_classes = []

# 局部启用
from user.authentications import JSONWebTokenAuthentication
authentication_classes = [JSONWebTokenAuthentication]

上述操作时针对于用户密码而言的,如果是多方式登陆如何利用jwt实现呢?

from django.contrib.auth.backends import ModelBackend
from .models import User
import re
class JWTModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        """
        :param request:
        :param username: 前台传入的用户名,可以是多方式下的数据
        :param password: 前台传入的密码
        :param kwargs:
        :return:
        """
        try:
            if re.match(r'^1[3-9]d{9}$', username):
                user = User.objects.get(mobile=username)
            elif re.match(r'.*@.*', username):
                user = User.objects.get(email=username)
            else:
                user = User.objects.get(username=username)
        except User.DoesNotExist:
            return None  # 认证失败就返回None即可,jwt就无法删除token
        # 用户存在,密码校验通过,是活着的用户 is_active字段为1
        if user and user.check_password(password) and self.user_can_authenticate(user):
            return user  # 认证通过返回用户,交给jwt生成token
配置多方式登陆:   settings.py

AUTHENTICATION_BACKENDS = ['user.utils.JWTModelBackend']

手动签发jwt: 拥有原生登陆基于model类user对象签发 jwt

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)


# 了解,原生视图
# 原生APIView可以实现手动签发 jwt
class LoginAPIView(APIView):
def post(self):
# 完成手动签发
pass

原文地址:https://www.cnblogs.com/changwenjun-666/p/11574245.html