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.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTY5MjAyODc1LCJlbWFpbCI6IjEzMDYzNDczMzgwQDE2My5jb20ifQ.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