drf_jwt

Posted changwenjun-666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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]\d9$, 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]

 

以上是关于drf_jwt的主要内容,如果未能解决你的问题,请参考以下文章