03-登录接口实现

一、安装djangorestframework-jwt拓展
pip install djangorestframework-jwt

编辑dev.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
      	# 追加Token认证后端 —— 用于验证token有效期识别用户身份
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

JWT_AUTH = {
  	# 有效期设置为10天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=10),
}
二、手动定义序列化器和视图实现

我们先尝试着自己写视图和序列化器来完成传统身份认证登陆接口;

此处我们需要体会:在DRF编程中,我们需要尽可能使用序列化器来完成主要业务代码,视图仅仅用做映射路由,调用序列器来完成主要业务!简单一句话就是 —— 把视图的业务代码剥离开,统一组织到序列化器中实现

1、新建apps/meiduo_admin/serializers/loginserializers.py
from django.contrib.auth import authenticate
from rest_framework import serializers
from rest_framework_jwt.utils import jwt_payload_handler,jwt_encode_handler

# 明确,定义一个序列化器对username和password这两个字段进行校验
# 校验的前端传参是:{"username": 'weiwei', 'password': 'xxxxxx'}
class LoginSerializer(serializers.Serializer):
    username = serializers.CharField(
        required=True,
        max_length=20,
        allow_blank=False,
        allow_null=False
    )
    password = serializers.CharField(
        required=True,
        max_length=20,
        min_length=8,
        allow_blank=False,
        allow_null=False
    )

    # 登陆校验用户名和密码 —— 自定义校验
    def validate(self, attrs):
        # attrs = {"username": 'weiwei', 'password': 'xxxxxx'}
        # 1、传统身份校验(校验用户和密码)
        user = authenticate(**attrs)
        if user is None:
            raise serializers.ValidationError("用户名或密码错误!")

        # 2、传统身份校验成功 —— 签发token(令牌) —— 把该token作为有效数据的一部分
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)

        # 3、返回有效数据
        return {
            'user': user,
            'token': token
        }
2、新建apps/meiduo_admin/views/login_views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from ..serializers.loginserializers import LoginSerializer

class LoginView(APIView):

    def post(self, request):
        # 1、提取参数
        # 2、校验参数
        # 3、数据处理
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 4、构建响应
        return Response({
            'token': serializer.validated_data.get('token'),
            'username': serializer.validated_data['user'].username,
            'user_id': serializer.validated_data['user'].id
        })
3、编辑apps/meiduo_admin/urls.py
from django.urls import re_path
from meiduo_admin.views import login_views

urlpatterns = [
    # 登陆路由
	re_path(r'^authorizations/$', login_views.LoginView.as_view()),
]
三、使用djangorestframework-jwt拓展提供的视图接口完成
  • 前面我们是自己写视图和序列化器来完成传统身份认证登陆接口,其实Django已经提供有封装好的函数供我们调用,不需要自己写代码实现序列器和视图函数进行身份认证登录。
1、编辑apps/meiduo_admin/urls.py映射接口视图

导包:from rest_framework_jwt.views import obtain_jwt_token

from django.urls import re_path
from meiduo_admin.views import login_views
# obtain_jwt_token为拓展插件提供的用于验证用户名和密码并签发token的视图
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    # 登陆路由
    # re_path(r'^authorizations/$', login_views.LoginView.as_view()),
    re_path(r'^authorizations/$', obtain_jwt_token),
]

测试登录:
在这里插入图片描述

  • 我们发现,返回的数据只有token,它返回的数据格式是由方法jwt_response_payload_handler返回的数据决定的,不符合业务需求,前端要求我们返回的数据有:tokenusernameuser_id
    因此,我们需要重写jwt_response_payload_handler方法
2、新建meiduo_mall/utils/jwt_response_handlers.py模块,自定义obtain_jwt_token视图中用于构造响应的函数
"""
重写后台管理系统身份登陆校验方法的响应数据
"""

def jwt_response_payload_handler(token, user=None, request=None):
    return {
        # 补充返回的username和user_id字段
        'username': user.username,
        'user_id': user.id,
        'token': token
    }
3、编辑dev.py
JWT_AUTH = {
    # 设置签发的token的有效期
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=100),

    # 来指定拓展插件默认视图返回的响应参数构造函数
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'meiduo_mall.utils.jwt_response_handlers.jwt_response_payload_handler'
}
  • 测试如下:返回的数据格式已经变成了我们业务需求的格式
    在这里插入图片描述
四、登录接口扩展(权限限定)
1、业务需求:后台管理站点只允许员工/管理员(is_staff=True)的账号登陆签发token;
商城页面8080
Django后台
后台管理页面8081
调用全局authenticate函数实现用户名和密码校验
自定义的传统身份认证后端完成校验
判断是否是管理站点页面登陆
校验is_staff=True
直接放行
2、编辑apps/users/utils.py
# 继承Django默认的传统认证后端
class UsernameMobileAuthBackend(ModelBackend):

    # 重写authenticate方法
    # 原因:默认的authenticate方法,只会根据username字段去过滤查找用户
    def authenticate(self, request, username=None, password=None, **kwargs):
        # 允许多账号登陆的情况下,前端传来的"username"有可能是用户名也有可能是手机号

        try:
            # 1、先按用户名查找
            user = User.objects.get(
                # username=="18588269037" or mobile=="18588269037"
                Q(username=username) |  Q(mobile=username) | Q(email=username)
            )
        except User.DoesNotExist as e:
            return None # 用户名找不到,返回None表示认证失败


        # TODO:判断是否为管理站点页面登陆,如果是需要进一步校验is_staff=True
        # 如果是商城页面登陆,request是一个请求对象
        # 如果是管理站点页面登陆,request是一个None
        if request is None and not user.is_staff:
            return None

        # 3、某一个找到了,再校验密码
        if user.check_password(password):
            return user
  • 这里有个问题,为什么:
    如果是商城页面登陆,request是一个请求对象?
    如果是管理站点页面登陆,request是一个None?

当我们进行登录请求后端时,都会调用重写的authenticate方法进行校验,该方法默认参数request=None,而我们在之前开发商城登录接口的时候,手动给参数request赋了一个request值:

  • 源代码authenticate方法
    在这里插入图片描述
  • 之前写的登录接口代码:
    在这里插入图片描述

所以:

  • 如果是商城页面登陆,request是一个请求对象
  • 如果是管理站点页面登陆,request是一个None
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值