DjangoRestFramework实现JWT登录,并将token缓存到redis中
-
创建一个Django应用FirstApp,并实现用户注册接口,用户注册功能实现见上篇用户注册,调用接口创建一个用户数据(包含用户名密码),项目结构如下
-
安装DjangoJWT
- pip3 install djangorestframework-jwt
- pip3 install djangorestframework-jwt
-
项目目录下settings.py文件中配置DjangoJWT
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}
JWT_AUTH = {
# token有效期为24小时
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}
-
安装Django redis
- pip3 install django-redis
- pip3 install django-redis
-
项目目录下settings.py文件中配置redis缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://@127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
- 应用中设计urls.py
# -*-coding:utf-8-*-
from rest_framework.routers import DefaultRouter
from FirstApp import views
router = DefaultRouter()
router.register(r'api/auth/tokens', views.TokenViewSet)
- 应用中设计User模型类,这里重点写的是设置密码和校验密码的方法,后面JWT登录校验密码时会用到check_password方法
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
from utils.encrypt_password import encrypt_password
class UUIDTools(object):
@staticmethod
def uuid4_hex():
return uuid.uuid4().hex
class User(models.Model):
# default指定的是一个类,每次会创建一个新的对象,然后调用相关方法
id = models.UUIDField(primary_key=True, auto_created=True, default=UUIDTools.uuid4_hex, editable=False)
username = models.CharField(max_length=32, unique=True)
password = models.CharField(max_length=256)
# null=True, blank=True, 表示创建用户时该字段为可选字段
mobile = models.CharField(max_length=11, blank=True, unique=True)
email = models.EmailField(max_length=64, blank=True, unique=True)
def set_password(self, password):
self.password = encrypt_password(password)
def check_password(self, password):
return self.password == encrypt_password(password)
- 设计序列化器类UserSerializer
# -*-coding:utf-8-*-
from rest_framework import serializers
from FirstApp.models import User
class UserSerializer(serializers.ModelSerializer):
# style表示前台输入是密文,write_only表示序列化时不会序列化该字段
password = serializers.CharField(style={'input_type': 'password'}, write_only=True, max_length=256)
class Meta:
model = User
fields = ('id', 'username', 'password', 'mobile', 'email')
# 创建用户时更新密码为密文
def create(self, validated_data):
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
# 更新用户时更新密码为密文
def update(self, instance, validated_data):
user = super().update(instance, validated_data)
if 'password' in validated_data.keys():
user.set_password(validated_data['password'])
user.save()
return user
# 重写to_representation方法,自定义响应中的json数据
def to_representation(self, instance):
# 返回结果中id字段中间有横线,需要去除
ret = super().to_representation(instance)
ret['id'] = ret['id'].replace('-', '')
return ret
- 应用中
views.py
设计视图类TokenViewSet
# -*-coding:utf-8-*-
from django.core.cache import cache
from rest_framework import status, exceptions
from rest_framework.mixins import CreateModelMixin
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework_jwt.settings import api_settings
from FirstApp.models import User
from FirstApp.serializers import UserSerializer
class TokenViewSet(CreateModelMixin, GenericViewSet):
queryset = User.objects.all()
def create(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
# 校验用户名和密码
users = User.objects.filter(username=username)
if not users.exists():
raise exceptions.NotFound(detail='用户不存在')
user = users.first()
if not user.check_password(password):
raise exceptions.ValidationError(detail='密码错误')
# 手动签发token
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)
# 将token存到redis缓存中
cache.set(token, user, 60 * 60 * 24)
user_id = str(user.id).replace('-', '')
# 响应数据
data = {
'token': token,
'user_id': user_id,
'user_name': user.username
}
headers = self.get_success_headers(data)
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
- postman通过用户名密码获取token,效果图如下
我在想我要不要写一句励志的话......