一:管理员登录:
<一>: 准备工作:
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

浙公网安备 33010602011771号