djangorestframework-simplejwt简单使用

我为什么要使用djangorestframework-simplejwt

现在Django-rest-framework-jwt网上有许多文章
但是现在Django-rest-framework-jwt停止维护了

image.png

djangorestframework-simplejwt详细文档

安装

pip install djangorestframework_simplejwt

设置

在settings.py中

# drf配置
REST_FRAMEWORK = {
    # JWT
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),

}

# JWT配置 里面具体配置可以参考文档
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=7),  # 配置过期时间
    'REFRESH_TOKEN_LIFETIME': timedelta(days=15),
}

在主目录下面的urls.py

"""pine_mountain_bridge URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from django.views.static import serve



urlpatterns = [
    path('admin/', admin.site.urls),

    # rest_framework_simplejwt自带的得到token
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    # 刷新JWT
    path('api/v1/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 验证token
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

得到token数据返回
refresh书用来刷新的
access是token

{
        "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU4NjMxMjYyOSwianRpIjoiZWUzNTQxNmIzMDZjNDIyY2EyOWFiOGQ5NjhlNjc3ZWYiLCJ1c2VyX2lkIjoxfQ.th5AfYNsd9UK3zL9LEhnDRNrd37Ut8ucl-G9VzYlCHI",
        "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg1NjIxNDI5LCJqdGkiOiJlYmI3ZDdmOTBlNzU0OTNkYjM4NTNiNTJkYzA0Yjk3NiIsInVzZXJfaWQiOjF9.WeoTxQ2rG0BOl9ji6KO3yfLm83kcsHV1drFDxGxAvGA",
}

刷新得到的数据

{
        "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg1NjIxNTIyLCJqdGkiOiI4MzcyN2UwYWVhMDU0MWYyYmE1ODljOWY5NTMzYjE2OSIsInVzZXJfaWQiOjF9.NTo1YKFl9tvcCYQVmBWLz0rhRL8044qT65jbe7okeG0"
    }

自定义djangorestframework-simplejwt

在你的serializers.py

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer


class MyTokenObtainPairSerializer(TokenObtainPairSerializer):

    '''
    token验证
    '''
    def validate(self, attrs):
        data = super().validate(attrs)

        refresh = self.get_token(self.user)

        data['refresh'] = str(refresh)
        data['access'] = str(refresh.access_token)
        data['username'] = self.user.username #这个是你的自定义返回的
        data['user_id'] = self.user.id #这个是你的自定义返回的

        return data


在你的views.py

from rest_framework_simplejwt.views import TokenViewBase
from rest_framework_simplejwt.serializers import TokenRefreshSerializer

from .serializers import *
from .models import *


class MyTokenObtainPairView(TokenObtainPairView):
    """
    自定义得到token username: 账号或者密码 password: 密码或者验证码
    """
    serializer_class = MyTokenObtainPairSerializer


class MyTokenRefreshView(TokenViewBase):
    """
    自定义刷新token refresh: 刷新token的元素
    """
    serializer_class = TokenRefreshSerializer

PS 记得写url。我这里就不写了

得到token

{
    "msg": "success",
    "code": 1,
    "data": {
        "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU4NjMxMjYyOSwianRpIjoiZWUzNTQxNmIzMDZjNDIyY2EyOWFiOGQ5NjhlNjc3ZWYiLCJ1c2VyX2lkIjoxfQ.th5AfYNsd9UK3zL9LEhnDRNrd37Ut8ucl-G9VzYlCHI",
        "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg1NjIxNDI5LCJqdGkiOiJlYmI3ZDdmOTBlNzU0OTNkYjM4NTNiNTJkYzA0Yjk3NiIsInVzZXJfaWQiOjF9.WeoTxQ2rG0BOl9ji6KO3yfLm83kcsHV1drFDxGxAvGA",
        "username": "hahn",
        "user_id": 1
    }
}

刷新token

{
    "msg": "success",
    "code": 1,
    "data": {
        "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg1NjIxNTIyLCJqdGkiOiI4MzcyN2UwYWVhMDU0MWYyYmE1ODljOWY5NTMzYjE2OSIsInVzZXJfaWQiOjF9.NTo1YKFl9tvcCYQVmBWLz0rhRL8044qT65jbe7okeG0"
    }
}

上面自定义数据结构的传送门

接口验证

在你的views.py中

class UserViewSet(CacheResponseMixin, viewsets.ModelViewSet):
    """
    测试的Api1
    """
    permission_classes = [permissions.IsAuthenticated]  # 权限认证主要是这个
    queryset = User.objects.all()
    serializer_class = UserSerializer
    throttle_classes = []

    # 自定义分页
    pagination_class = LisPagination

错误返回

{
    "msg": "身份认证信息未提供。",
    "state": 0,
    "data": ""
}

这个token是加在请求头的Authorization
太长就截图看了

截屏2020-03-24上午10.35.52.png
截屏2020-03-24上午10.36.53.png

现在自定义token接口完成了

但是drf jwt默认验证的是username和password
突然想验证手机号码或者其他什么的只是自定义了
PS:本来想换请求数据中的key的、就是不要username、password。因为能力有限暂时不知道怎么换。看了好久源码好像是和Django user模型绑定了

在settings.py中
pine_mountain_bridge里面的utils新建CustomToken.py

# 自定义JWT校验
AUTHENTICATION_BACKENDS = (
    'pine_mountain_bridge.utils.CustomToken.CustomBackend',
)

CustomToken.py

# 自定义得到token校验

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from rest_framework import serializers


from users.models import *


class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        # print(request.data) 参考请求的其他数据
        # print(request.data['demo']) 比如说key是demo的数据用来做你要的数据校验
        try:
            # 小编这里添加了一个手机验证,如果需要其他验证再加就ok了
            try:
                user = Users.objects.get(Q(username=username) | Q(mobile=username))
            except Exception:
                raise serializers.ValidationError({'': '账号没有注册'})

            if user.check_password(password):
                return user
            else:
                # 如果不想密码登录也可以验证码在这里写
                # 这里做验证码的操作
                raise serializers.ValidationError({'': '密码错误'})

        except Exception as e:
            raise e


返回数据

截屏2020-03-24上午10.45.40.png

jwt解码

根据token返回对于的数据
在你的serializers.py

from rest_framework_simplejwt.serializers import TokenVerifySerializer
from jwt import decode as jwt_decode

class MyTokenVerifySerializer(TokenVerifySerializer):
    """
    token验证
    """

    def validate(self, attrs):
        """
        attrs['token']: 是请求的token
        settings.SECRET_KEY: setting.py默认的key 除非在配置文件中修改了
        algorithms: 加密的方法
        """
        decoded_data = jwt_decode(attrs['token'], settings.SECRET_KEY, algorithms=["HS256"])
        return decoded_data

在views.py

from rest_framework_simplejwt.views import TokenObtainPairView, TokenViewBase
class MyTokenVerifyView(TokenViewBase):
    """
    验证token得到用户信息 token: 验证的token
    """
    serializer_class = MyTokenVerifySerializer

返回的数据
user_id就是你的用户ID

{
    "msg": "success",
    "status": 200,
    "data": {
        "token_type": "access",
        "exp": 1585710741,
        "jti": "97b42a6570d84108bf09701a60fbf5c1",
        "user_id": 1
    }
}

djangorestframework-simplejwt退出登录

JWT是不带退出内置的办法的
JWT退出登录很多方法
我这里是使用Django-redis库、用redis管理JWT的状态。用用户作为key保存token。
因为每个人的业务逻辑不一样、我就贴一些关键的代码

在setting.py

# redis配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "",
        }
    }
}

# redis缓存时间
REDIS_TIMEOUT = 60*60*24*7

使用

from django.core.cache import cache

保存redis缓存

        # 设置redis缓存
        # redis_key你的key
        # token token
        # REDIS_TIMEOUT 保存的数据
        cache.set(redis_key, token, REDIS_TIMEOUT)

清除redis缓存
我这里是把value变成空了、方便我判断

        # 设置redis缓存
        # redis_key你的key
        # REDIS_TIMEOUT 保存的数据
        cache.set(redis_key,'', REDIS_TIMEOUT)

查看redis缓存

# 解码token得到user_id
decoded_data = jwt_decode(token algorithms=["HS256"])
# redis_key 你设置的redis的key
results = cache.get(redis_key)
# 判断、写自己的业务逻辑
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容