飞行的猪哼哼

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一:管理员登录:

<一>: 准备工作:
1:安装djangorestframework-jwt拓展

pip install djangorestframework-jwt

2:编辑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
2: 新建apps/meiduo_admin/serializers/loginserializers.py
思路:新建一个序列化器的包:serializers, 然后在里面定义序列化器。 首先第一个登录接口,我们需要序列化器对我们的username,和password 进行校验。校验流程: 先对username和password进行常规校验,如果校验成功,使用自定义登录后端对用户名密码进行校验,这个时候就需要自定义校验了,可以采用最终的校验:validate(self,attr)这个函数进行校验,如果校验失败,需要DRF抛出异常:ValidatedError , 如果校验成功,则接下来需要给用户办法令牌。令牌的颁发需要使用:
from rest_framework_jwt.utils import jwt_payload_handler,jwt_encode_handler

from django.contrib.auth import authenticate
from rest_framework import serializers

# 目的定义一个序列化器对username 和password进行校验
# 这里由于不设及模型类所以继承serializers.Serializer
from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler


class LoginSerializer(serializers.Serializer):
    
    username = serializers.CharField(required=True),
    password = serializers.CharField(required=True),
    
    # 接下来要使用登陆验证后端来校验用户名和密码,此时就需要自定义校验---采用最终的校验
    def validate(self, attrs):
        # 由于传入的是用户名和密码的字典格式,所以可以直接拆包
        user = authenticate(**attrs)
        
        if user is None:
            raise serializers.ValidationError("用户名或者密码错误!!!")
        
        # 如果校验成功,那么就要给顾客办法令牌
        # 传入用户对象,得到荷载对象
        payload = jwt_payload_handler(user)
        # 传入荷载对象,得到令牌对象
        tocken = jwt_encode_handler(payload)
        
        return {
            'user': user,
            "tocken": tocken
        }
    

3:新建apps/meiduo_admin/views/login_views.py

from rest_framework.response import Response
from rest_framework.views import APIView

# 需要继承哪个呢?---不需要对数据进行增删改查,所以只需继承APIView
from apps.meiduo_admin.serializers.loginserializers import LoginSerializer


class LoginView(APIView):

    def post(self, request):
        serializer = LoginSerializer(data=request.data)
        # 传入raise_exception表示如果校验失败则抛出异常
        serializer.is_valid(raise_exception=True)

        return Response({
            'token': serializer.validated_data.get('token'),
            'username': serializer.validated_data['user'].username,
            'user_id': serializer.validated_data['user'].id
        })

<三>: 使用djangorestframework-jwt拓展提供的视图接口完成:
1:编辑apps/meiduo_admin/urls.py映射接口视图:

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

urlpatterns = [
    # re_path(r'^authorizations/$', LoginView.as_view()),
    re_path(r'^authorizations/$', obtain_jwt_token),
]

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'
}

<四>: 前端保存tocken的方式:
1:前端保存tocken的方式:

  • (1)、localStorage: 长期存储
  • (2)、sessionStorage: 临时存储(当浏览器关闭或者标签页关闭,数据清除)
    在美多后台管理业务中的存储为:
localStorage.token = response.data.token;
localStorage.username = response.data.username;
localStorage.user_id = response.data.user_id;

2:
rest_framework_jwt拓展的身份认证后端JSONWebTokenAuthentication所约定的前端传递token的方式:

  • (1)、在请求头中携带token值
  • (2)、具体约定的格式为
    在这里插入图片描述
    3:前端工程师在调用接口的时候,必须按照上述既定的格式传递token参数,用于后端身份认证
this.axios.get(cons.apis + '/goods/brands/'+this.edit_id+'/', {
      headers: {
        // 请求头中携带token
        'Authorization': 'JWT ' + token
      },
      responseType: 'json',
  })
  .then(dat=>{
     this.BrandsForm.name = dat.data.name;
     this.BrandsForm.logo = dat.data.logo;
     this.BrandsForm.first_letter = dat.data.first_letter;
  }).catch(err=>{
     console.log(err.response);
});

4:后端验证tocken:
rest_framework_jwt拓展插件提供的JSONWebTokenAuthentication认证后端自动认证

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 验证用户的令牌(token)来确定用户身份
        # 思考:
        # 1、前端如何传递token值;
        # 答: 头部携带;Authorization: JWT gregtfefewfewfewrw.trhyt4hy.4hy46h6fewfewfewfew
        #      JSONWebTokenAuthentication后端会根据既定的格式提取并校验token有效性,进而获取用户身份
        #      把验证成功的用户对象赋值给了request.user
        # 2、如何校验; —— 重复加密,比对新旧签名;
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

二:权限限定:

<一>: 后台管理站点只允许员工/管理员(is_staff=True)的账号登陆签发token。
思路:不管是来自8080的还是来自8081的都要经过django后端,最后调用全局authenticate函数实现用户名和密码的校验。都要经过我们自己定义的传统身份认证后端进行认证,所以我们只需要在我们自定义后端判断是不是8081的,如果是判断is_staff是不是True,如果来自8080直接放行。
在这里插入图片描述
编辑apps/users/utils.py
核心代码:

 # TODO:判断是否为管理站点页面登陆,如果是需要进一步校验is_staff=True
        # 如果是商城页面登陆,request是一个请求对象
        # 如果是管理站点页面登陆,request是一个None
        if request is None and not user.is_staff:
            return None
# 继承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
posted on 2020-09-26 20:59  飞行的猪哼哼  阅读(31)  评论(0)    收藏  举报