jwt自定义表签发
1、导入模块:
from rest_framework_jwt.settings import api_settings
2、写一个属性:
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
3、登录逻辑:
class UserViews(ViewSet):
@action(methods=["POST"], detail=False)
def login(self, request, *args, **kwargs):
username = request.data.get("username")
password = request.data.get("password")
user = User.objects.filter(username=username, password=password).first()
print(user)
if user:
# 通过user生成payload----->jwt提供的方法,但是用户名的字段必须是username,传入user,生成payload
payload = jwt_payload_handler(user)
# 生成token ----->jwt提供了方法,把payload传入------>生成token
token = jwt_encode_handler(payload)
return Response({"code": 100, "msg": "登录成功", "username": user.username, "token": token})
else:
return Response({"code": 101, "msg": "登录失败,账号或密码输入错误"})
路由层
from django.urls import path, include
from app_one.views import UserViews
from rest_framework.routers import SimpleRouter
render = SimpleRouter()
render.register("users", UserViews, "users")
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(render.urls)),
]
总结
基于自定义的用户表,签发token:
-1、前端----->发起http请求,携带用户的用户名和密码----->到后端
-2、后端从request.data中取出前端传入的数据----->字典格式------>取出用户名和密码
-3、拿着用户名和密码去数据库中查询有没有该用户
-4、如果有签发token
-4.1、通过当前用户得到payload(自己生成荷载)
-4.2、通过荷载生成token
-5、返回给前端{"code":100,"msg":"登录成功","token":token,"username":user.username}
-6、如果查不到该用户,返回用户名或密码输入错误
jwt 多方式登录(auth的user表)
1、用户名+密码、邮箱+密码、手机号+密码 都可以登录
username+password、email+password、phone+password
无论是username,email,phone都以 username形式提交到后端
于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码--->都能登录成功
2、auth的user签发
3、签发、校验用户逻辑------>放在序列化类中
4、存在一个问题--->已经迁移过表了--->已经存在auth的user表了,如果再去继承AbstractUser,再写用户表,就会出错
-解决方案:以后尽量不要这么做
-以后要扩写auth的user表,一开始就要扩写,不要等迁移完之后再扩写
-删库
-删迁移文件(不要删__init__.py和migrations文件夹)
-项目app的迁移文件
-django内置app的admin和auth的迁移文件
-重新迁移--两条命令
-扩写auth的user表,需要在配置文件配置 ###重要
5、创建超级用户----->创建到扩写的表中 auth的user-->AuthUser
python manage.py createsuperuser
面条版
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
from rest_framework_jwt.settings import api_settings
from rest_framework.response import Response
from .serializer import UserSerializer
from .models import AuthUser
import re
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class AuthUserView(GenericViewSet):
serializer_class = UserSerializer
@action(methods=["POST"], detail=False)
def login(self, request, *args, **kwargs):
username = request.data.get("username")
password = request.data.get("password")
if re.match(r'^1[3-9][0-9]{9}$', username):
user = AuthUser.objects.filter(phone=username).fister()
elif re.match(r'^.+@.+$', username):
user = AuthUser.objects.filter(email=username).first()
else:
user = AuthUser.objects.filter(username=username).first()
if user and user.check_password(password):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({"code": 100, "msg": "登录成功", "token": token, "username": user.username})
else:
return Response({"code": 101, "msg": "登录失败,账号或密码失败"})
封装版本:视图层
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
from rest_framework_jwt.settings import api_settings
from rest_framework.response import Response
from .serializer import UserSerializer
from .models import AuthUser
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class AuthUserView(GenericViewSet):
serializer_class = UserSerializer
@action(methods=["POST"], detail=False)
def login(self, request, *args, **kwargs):
# 拿到前端传入的用户名和密码,得到一个序列化类对象
user_serializer = self.get_serializer(data=request.data)
if user_serializer.is_valid():
token = user_serializer.context.get("token")
username = user_serializer.context.get("username")
return Response({"code": 100, "msg": "登录成功", "token": token, "username": username})
else:
return Response({"code": 101, "msg": "账号或密码输入错误"})
序列化类
from rest_framework import serializers
from .models import AuthUser
from rest_framework_jwt.settings import api_settings
from rest_framework.exceptions import AuthenticationFailed
import re
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class UserSerializer(serializers.ModelSerializer):
# 需要重写字段,不重写,字段自己规则过不了
username = serializers.CharField()
class Meta:
model = AuthUser
fields = ["username", "password"]
# username有字段自己的规则--->唯一 unique--->去数据库查询发现有lqz,之间字段自己规则报错了,不会走到全局钩子
def _get_user(self, attrs):
# 从校验的数据中取出username、password
username = attrs.get("username")
password = attrs.get("password")
# 通过正则来匹配我的username是手机号还是邮箱还是用户名
if re.match(r'^1[3-9][0-9]{9}$', username):
user = AuthUser.objects.filter(phone=username).fister()
elif re.match(r'^.+@.+$', username):
user = AuthUser.objects.filter(email=username).first()
else:
user = AuthUser.objects.filter(username=username).first()
# 如果我的用户对象以及我加密后的密码都为True,将这个user对象返回
if user and user.check_password(password):
return user
# 否则主动抛出异常
else:
raise AuthenticationFailed("账号或密码输入错误")
def _get_token(self, user):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return token
def validate(self, attrs):
# 如果返回user,说明,用户名密码对了,如果没走到这里,说明抛异常,抛异常说明用户名密码错误
user = self._get_user(attrs)
token = self._get_token(user)
# 放在这里面 self 是序列化类的对象 ,context 是空字典,它是 视图类和序列化类之间沟通的桥梁
self.context["username"] = user.username
self.context["token"] = token
return attrs
路由层
from django.contrib import admin
from django.urls import path, include
from app_one.views import AuthUserView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register("user", AuthUserView, "user")
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
总结
# 序列化,反序列化,数据校验--->只用来做数据校验
# 前端传过来的字段,都要,而且要校验 :username password
# 只要视图类中执行 ser.is_valid():
会走字段自己的规则--->username过不了--->因为有unique--->所有需要重写
会走局部钩子--->咱们没写
会走全局钩子--->全局钩子里校验
-分成了两个方法:好处是以后修改方法
-_get_user :多方式的 ,以后改成单方式登录,只要该这个方法即可
-_get_token:用的第三方签发,后期改成自己的签发,只需要改它即可
-把生成的token和用户名放到了,序列化类中,单是怕污染数据,放到了序列化类的对象的context中
-self.context["username"] = user.username
-self.context["token"] = token
# 视图类中取出来
token = ser.context.get('token')
username = ser.context.get('username')
作业
# 1 自定义用户表,普通签发
# 2 扩写authuser表,多方式登录
# 3 自己的用户表,多方式登录
# -----自定义认证类-----