Ⅰ simple-jwt快速使用
# django框架上,第三方的jwt解决方案
# django-rest-framework-jwt 老的,不更新了
# djangorestframework-simplejwt 新的,一直更新
【一】安装
pip3 install djangorestframework-simplejwt
【二】快速使用
# 【1】 创建超级用户
python manage.py createsuperuser
# 【2】 配置路由urls.py
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
# 【3】 postman测试
向后端接口发送post请求,携带用户名密码,即可看到生成的token

【三】双token认证
【1】双token介绍
-一个token 时间很久 比如 7天 refresh
-一个token 时间很多 比如 3分钟 access
-只要登录后才能访问的接口-->要携带 access--token-->3分钟后过期了,不能用了
-一旦access过期,发送请求携带refresh-->token--->通过refresh-->签发一个新的access-->发送请求使用access

import base64
# 分别放入就行
res=base64.b64decode("eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzIyNzQ2MDYzLCJpYXQiOjE3MjI3NDU3NjMsImp0aSI6IjkwYjk0ZDU5NjUwZTQ0YWI4ODlkNDBhYzEwZjJlYjViIiwidXNlcl9pZCI6MX0=")
print(res) # {"token_type":"access","exp":1722746063,"iat":1722745763,"jti":"90b94d59650e44ab889d40ac10f2eb5b","user_id":1}
【2】双token认证使用
# 快速使用->django内置的auth-user表作为用户表-->签发和认证
-签发:路由配置-->帮写了登录
from rest_framework_simplejwt.views import token_obtain_pair
urlpatterns = [
path('login/', token_obtain_pair),
]
-认证:
class BookView(APIView):
authentication_classes = [JWTTokenUserAuthentication]
permission_classes = [IsAuthenticated]
-如果不加权限类,执行逻辑是:
-如果携带了token--->就去校验-->如果有错就会报错
-如果没有携带token-->根本不校验-->所以如果只配置JWTTokenUserAuthentication,用户不带登录信息,是不做校验的
# 就相当于你带身份证去网吧 有身份证才会进行校验,但是不带的也能进去,后续还会有别的在校验
# 所以只有有身份证之后 才会后续畅通无阻的进去
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTTokenUserAuthentication
from rest_framework.permissions import IsAuthenticated
class BookView(APIView):
authentication_classes = [JWTTokenUserAuthentication]
permission_classes = [IsAuthenticated]
def get(self,request):
return Response("很多玉树")

- 只不过携带的不是token是Authorization
key:Authorization
value:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzIyNzQ3NDc2LCJpYXQiOjE3MjI3NDcxNzYsImp0aSI6ImJhYzIzMTk3M2E1ZDQ1ZjhiMmY1MGZjMzU3NzM0NjUyIiwidXNlcl9pZCI6MX0.dkZhY19kbR1g7XKwaDOrxZHBhWJJQ40Liid5f3VK7aU
# 固定句式 Bearer加空格加 access
# Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzIyNzQ3NDc2LCJpYXQiOjE3MjI3NDcxNzYsImp0aSI6ImJhYzIzMTk3M2E1ZDQ1ZjhiMmY1MGZjMzU3NzM0NjUyIiwidXNlcl9pZCI6MX0.dkZhY19kbR1g7XKwaDOrxZHBhWJJQ40Liid5f3VK7aU
【四】更新access
# 路由
urlpatterns = [
path('refresh/', token_refresh),
]
# 访问 http://localhost:8000/refresh/


Ⅱ simple-jwt配置文件
# JWT配置
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), # Access Token的有效期
'REFRESH_TOKEN_LIFETIME': timedelta(days=7), # Refresh Token的有效期
# 对于大部分情况,设置以上两项就可以了,以下为默认配置项目,可根据需要进行调整
# 是否自动刷新Refresh Token
'ROTATE_REFRESH_TOKENS': False,
# 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌。需要将'rest_framework_simplejwt.token_blacklist'加入到'INSTALLED_APPS'的配置中
'BLACKLIST_AFTER_ROTATION': False,
'ALGORITHM': 'HS256', # 加密算法
'SIGNING_KEY': settings.SECRET_KEY, # 签名密匙,这里使用Django的SECRET_KEY
# 如为True,则在每次使用访问令牌进行身份验证时,更新用户最后登录时间
"UPDATE_LAST_LOGIN": False,
# 用于验证JWT签名的密钥返回的内容。可以是字符串形式的密钥,也可以是一个字典。
"VERIFYING_KEY": "",
"AUDIENCE": None,# JWT中的"Audience"声明,用于指定该JWT的预期接收者。
"ISSUER": None, # JWT中的"Issuer"声明,用于指定该JWT的发行者。
"JSON_ENCODER": None, # 用于序列化JWT负载的JSON编码器。默认为Django的JSON编码器。
"JWK_URL": None, # 包含公钥的URL,用于验证JWT签名。
"LEEWAY": 0, # 允许的时钟偏差量,以秒为单位。用于在验证JWT的过期时间和生效时间时考虑时钟偏差。
# 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer"
"AUTH_HEADER_TYPES": ("Bearer",),
# 包含JWT的HTTP请求头的名称。默认为"HTTP_AUTHORIZATION"
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
# 用户模型中用作用户ID的字段。默认为"id"。
"USER_ID_FIELD": "id",
# JWT负载中包含用户ID的声明。默认为"user_id"。
"USER_ID_CLAIM": "user_id",
# 用于指定用户身份验证规则的函数或方法。默认使用Django的默认身份验证方法进行身份验证。
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
# 用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken"。
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
# JWT负载中包含令牌类型的声明。默认为"token_type"。
"TOKEN_TYPE_CLAIM": "token_type",
# 用于指定可以使用的用户模型类。默认为"rest_framework_simplejwt.models.TokenUser"。
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
# JWT负载中包含JWT ID的声明。默认为"jti"。
"JTI_CLAIM": "jti",
# 在使用滑动令牌时,JWT负载中包含刷新令牌过期时间的声明。默认为"refresh_exp"。
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
# 滑动令牌的生命周期。默认为5分钟。
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
# 滑动令牌可以用于刷新的时间段。默认为1天。
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
# 用于生成访问令牌和刷新令牌的序列化器。
"TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
# 用于刷新访问令牌的序列化器。默认
"TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
# 用于验证令牌的序列化器。
"TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
# 用于列出或撤销已失效JWT的序列化器。
"TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
# 用于生成滑动令牌的序列化器。
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
# 用于刷新滑动令牌的序列化器。
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}
# 在项目配置文件中配置
from datetime import timedelta
SIMPLE_JWT={
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), # Access Token的有效期
'REFRESH_TOKEN_LIFETIME': timedelta(days=7), # Refresh Token的有效期
}
Ⅲ 定制登录返回格式
【一】定制返回格式
# 我们要求的格式-->token认证
{code:100,msg:登录成,token:asdfa.asdfa.asdfasd}
# 使用步骤:
1 写个序列化类,重写validate方法,方法返回什么,登录成功的格式就是什么
-如下
2 配置文件配置:
SIMPLE_JWT={
"TOKEN_OBTAIN_SERIALIZER": "app01.serialzier.MyTokenObtainPairSerializer",
}
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
"""
自定义返回的格式
self.user 就是当前登录用户
super().validate(attrs) 返回的数据是个字典:有refresh access
"""
old_data = super().validate(attrs)
data = {'code': 100,
'msg': '登录成功成功',
'username': self.user.username,
# 'refresh': old_data.get('refresh'),
'token': old_data.get('access'),
}
return data

【二】更改荷载
【1】模板
# 使用步骤:
【1】 写个序列化类,重写validate方法,重新一个类方法
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['name'] = user.username
return token
【2】 配置文件配置:
SIMPLE_JWT={
"TOKEN_OBTAIN_SERIALIZER": "aoo01.serialzier.MyTokenObtainPairSerializer",
}
【2】使用
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer # 最终继承的是serializers.Serializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['name'] = user.username
token['email'] = user.email
return token
def validate(self, attrs):
"""
自定义返回的格式
self.user 就是当前登录用户
super().validate(attrs) 返回的数据是个字典:有refresh access
"""
old_data = super().validate(attrs)
data = {'code': 100,
'msg': '登录成功',
'username': self.user.username,
'refresh': old_data.get('refresh'),
'token': old_data.get('access'),
}
return data
【2】 配置文件配置:
SIMPLE_JWT={
"TOKEN_OBTAIN_SERIALIZER": "aoo01.serialzier.MyTokenObtainPairSerializer",
}

import base64
res = base64.b64decode("eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzIyNzU1ODY4LCJpYXQiOjE3MjI3NTIyNjgsImp0aSI6ImUyNDExNGU1OWFhZjQ3MjJhNTRiNGIxYmFlNGIxMGNiIiwidXNlcl9pZCI6MSwibmFtZSI6Inp5YiIsImVtYWlsIjoiODhAcXEuY29tIn0=")
print(res)
# token_type":"access","exp":1722755868,"iat":1722752268,"jti":"e24114e59aaf4722a54b4b1bae4b10cb","user_id":1,"name":"zyb","email":"88@qq.com"}
Ⅳ 多方式登录
【一】关于迁移文件和扩写表字段
【1】扩写表字段迁移会报错
# 继承上面的代码继续 需要扩写auth的User表,增加手机号字段
-配置文件配置:# 扩写auth的user表,必须配置它
AUTH_USER_MODEL='aoo01.UserInfo'
-##这里有一个很大的坑## !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
如果要扩写auth的user表-->必须在没迁移之前就扩写
一旦迁移之后,再扩写就会出问题-->尽量不要这样做
-真的没办法了--尽量少用:毕竟是无法撤回的操作
1 删库--->(保存之前库的数据)
2 删除迁移文件
自己app的
django内置app的 admin 和auth

【2】删除迁移文件步骤
(1)先删库

(2)删除迁移文件

(3)删除django内置的



- 把源码admin下得migrations里面的迁移文件删除


【二】关于多方式登录
【1】扩写字段
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
mobile = models.CharField(max_length=11, default=True)
# 扩写auth的user表,必须配置它
AUTH_USER_MODEL='aoo01.UserInfo'
【2】关于多方式登录介绍
# 多方式登录
用户名+密码
手机号+密码
邮箱+密码
# 前端传,就要自己写登录了
{username:'zyb/15036883311/88@qq.com',password:'741'}
【三】代码实现
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTTokenUserAuthentication
from rest_framework.permissions import IsAuthenticated
class BookView(APIView):
authentication_classes = [JWTTokenUserAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
return Response("很多玉树")
from .serialzier import LoginSerialzier
class LoginView(APIView):
def post(self, request):
'''1 都写在视图类的方法中
1 取出用户名,密码
2 去数据库校验
3 校验通过,签发token
4 不通过返回错误信息
'''
'''2 换种思路
1 实例化得到序列化类对象
2 序列化类对象调用-->is_valid--->走字段自己,局部钩子,全局钩子校验
-全局钩子中:取出用户名,密码-->去数据库校验-->校验通过,签发token
3 is_valid通过,继续往下走,取出token
4 返回登录成功信息
'''
# 一条筋
# username = request.data.get('username')
# password = request.data.get('password')
# # 2 正则匹配是 手机号+密码 用户+密码 邮箱+密码
# import re
# if re.match(r'^1[3-9][0-9]{9}$', username):
# # 手机登录
# user = UserInfo.objects.filter(mobile=username).first()
# elif re.match(r'^.+@.+$', username):
# # 邮箱登录
# user = UserInfo.objects.filter(email=username).first()
# else:
# # 账号登录
# user = UserInfo.objects.filter(username=username).first()
#
# # 3 校验密码
# if user and user.check_password(password):
# # 签发token--》借助于simple-jwt
# # simple-jwt 提供的RefreshToken 可以根据用户对象,签发 refresh和access 的token值
# refresh = RefreshToken.for_user(user)
# refresh_token = str(refresh) # refresh
# access_token = str(refresh.access_token) # access
# return Response({'code': 100, 'msg': '登录成功', 'refresh': refresh_token, 'access': access_token})
# else:
# return Response({'code': 999, 'msg': '用户名或密码错误'})
serializer = LoginSerialzier(data=request.data)
serializer.is_valid(raise_exception=True)
refresh = serializer.context.get('refresh')
access = serializer.context.get('access')
return Response({'code': 100, 'msg': '登录成功', 'refresh': refresh, 'access': access})
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer # 最终继承的是serializers.Serializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['name'] = user.username
# token['email'] = user.email
return token
def validate(self, attrs):
"""
自定义返回的格式
self.user 就是当前登录用户
super().validate(attrs) 返回的数据是个字典:有refresh access
"""
old_data = super().validate(attrs)
data = {'code': 100,
'msg': '登录成功',
'username': self.user.username,
'refresh': old_data.get('refresh'),
'token': old_data.get('access'),
}
return data
from rest_framework import serializers
from .models import UserInfo
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.exceptions import APIException
class LoginSerialzier(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
# def _get_user(self, attrs):
# return user
def validate(self, attrs):
# 1 取出前端传入的用户名密码
username = attrs.get('username')
password = attrs.get('password')
# 2 正则匹配是 手机号+密码 用户+密码 邮箱+密码
import re
if re.match(r'^1[3-9][0-9]{9}$', username):
# 手机登录
user = UserInfo.objects.filter(mobile=username).first()
elif re.match(r'^.+@.+$', username):
# 邮箱登录
user = UserInfo.objects.filter(email=username).first()
else:
# 账号登录
user = UserInfo.objects.filter(username=username).first()
# 3 校验密码
if user and user.check_password(password):
# 签发token-->借助于simple-jwt
# simple-jwt 提供的RefreshToken 可以根据用户对象,签发 refresh和access 的token值
refresh = RefreshToken.for_user(user)
refresh_token = str(refresh) # refresh
access_token = str(refresh.access_token) # access
# self就是 LoginSerialzier序列化的对象 就是 serializer
self.context['refresh'] = refresh_token
self.context['access'] = access_token
return attrs
else:
raise APIException('用户名或密码错误')
from django.contrib import admin
from django.urls import path,include
from aoo01.views import LoginView
from rest_framework_simplejwt.views import token_obtain_pair,token_refresh
urlpatterns = [
path('admin/', admin.site.urls),
path('mul_login/', LoginView.as_view()),
path('login/', token_obtain_pair),
path('api/v1/', include('aoo01.urls')),
path('refresh/', token_refresh),
]

【四】总结
# 如果想用 AUTH的user表作为用户表,无论是否扩展该表
-1 认证:固定的
class BookView(APIView):
authentication_classes = [JWTTokenUserAuthentication]
permission_classes = [IsAuthenticated]
-2 签发:
-1 使用simple-jwt提供的
-修改返回格式
-修改荷载
-2 自定义 login,实现多方式登录-->基于auth的user表签发
# 未来,自定义用户表,签发和认证
-1 认证:自定义认证类
-2 签发:自己写
Ⅴ 自定义用户表-签发和认证
【一】签发
path('my_login/', MyLoginView.as_view()),
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32) # 没有加密--明文
age = models.CharField(max_length=32)
# 自定义用户表,签发token
from .serialzier import MyLoginSerialzier
class MyLoginView(GenericAPIView):
serializer_class = MyLoginSerialzier
def post(self,request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
refresh = serializer.context.get('refresh')
access = serializer.context.get('access')
return Response({'code': 100, 'msg': '登录成功', 'refresh': refresh, 'access': access})
# # 只用来做校验
from .models import User
class MyLoginSerialzier(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
user = User.objects.filter(username=username, password=password).first()
assert user, APIException('用户名密码错误')
# 签发token # 默认荷载有 user_id:id ,所以自定义用户表的用户id必须叫 id 要么就修改配置文件
refresh = RefreshToken.for_user(user)
self.context['refresh'] = str(refresh)
self.context['access'] = str(refresh.access_token)
return attrs
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc,context):
response=exception_handler(exc,context)
if response:
return Response({'code':999,'msg':'服务器异常,请稍后再试'})
else:
if isinstance(exc,AssertionError):
return Response({'code': 109, 'msg': str(exc)})
else:
return Response({'code': 888, 'msg':'稍后再试'})
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'aoo01.exception.common_exception_handler',
}
【二】认证
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import APIException
from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import User
# class JsonWebTokenCommonAuthentication(JWTAuthentication):
# def authenticate(self, request):
# token=request.META.get('HTTP_AUTHORIZATION')
# if token:
# # 验证token-->使用JWTAuthentication 提供的方法 get_validated_token
# res=self.get_validated_token(token) # 得到荷载
# # 从荷载中取出,用户id
# user_id=res.get('user_id')
# # 查询出当前用户
# user=User.objects.get(pk=user_id)
# return user,token
#
# else:
# raise APIException('没有携带认证信息')
#继承BaseAuthentication
from rest_framework_simplejwt.tokens import AccessToken
class JsonWebTokenCommonAuthentication(BaseAuthentication):
def authenticate(self, request):
token=request.META.get('HTTP_AUTHORIZATION')
if token:
# 验证token-->使用JWTAuthentication 提供的方法 get_validated_token
try:
res = AccessToken(token)
except Exception:
raise APIException('token认证失败')
# 从荷载中取出,用户id
user_id=res.get('user_id')
# 查询出当前用户
user=User.objects.get(pk=user_id)
return user,token
else:
raise APIException('没有携带认证信息')
# 使用
# 视图类配置
-不需要再搭配permission了
-放在请求头中:Authorization:adsfasfd.asdfasdf.asfdasd
class BookView(APIView):
authentication_classes = [JsonWebTokenCommonAuthentication]
# 全局配置
from .auth import JsonWebTokenCommonAuthentication
class BookView(APIView):
authentication_classes = [JsonWebTokenCommonAuthentication]
def get(self, request):
return Response('哈哈哈哈哈好吧')

Ⅵ 权限介绍
# 工作中得权限有 三大类
- 访问控制列表:ACL-->针对于互联网用户
-抖音:评论,点赞,开直播
张三用户:[评论,点赞]
李四用户:[评论,点赞,开直播]
- RBAC 基于角色的访问控制-->公司内部
-开发部:张三 李四
[删除代码,查看代码]
-运维部:王五
[操作服务器]
-总裁办:lqz
[发工资]
- ABAC:基于属性的访问控制-->在rbac基础上扩展
RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。