Django 权限系统项目实战详细图文教程 4--系统登录功能实现 2

来源引用网络知识与某站曹老师视频相互结合学习记录,仅供参考!

登录功能实现

三、后端登录业务功能逻辑实现

16、打开 Django 后端项目,修改"user"子模块下的"views.py"文件内容,新建 LoginView 类;

复制代码
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from rest_framework_jwt.settings import api_settings

from user.models import SysUser


# Create your views here.

# 定义TestView类(导包引入from django.views import View)
class TestView(View):

    def get(self, request):
        # 获取token,需注意在前端是叫"AUTHORIZATION"这个名字,而到Django中则会自动添加"HTTP_"变为"HTTP_AUTHORIZATION"
        token = request.META.get('HTTP_AUTHORIZATION')
        print(f'打印查看token值: {token}')
        # 判断token不等于None值且不为空值,就表示通过了
        if token != None and token != '':
            # 导包引入from user.models import SysUser,通过objects.all()查询所有用户信息
            userList_obj = SysUser.objects.all()
            print(f'打印查看userList_obj类型:{type(userList_obj)},查看: {userList_obj},')
            # 转存字典,里面的对象转换为了字典类型
            userList_dict = userList_obj.values()
            print(f'打印查看userList_dict2类型: {type(userList_dict)},查看:{userList_dict}')
            # 把外层的容器转为List
            userList = list(userList_dict)
            print(f'打印查看userList类型: {type(userList)},查看:{userList}')
            # 导包引入from django.http import JsonResponse
            return JsonResponse({'code': 200, 'info': '测试', 'data': userList})
        else:
            # 没有通过的话就给前端返回401
            return JsonResponse({'code': 401, 'info': '没有测试访问权限!'})

# 定义一个 JwtTestView 测试类,模拟登录
class JwtTestView(View):

    # get请求方式
    def get(self, request):
        # 这里模拟登录是直接从之前添加测试数据的表中获取,返回的是一个用户user
        user = SysUser.objects.get(username='jack', password='123456')
        # 导包引入from rest_framework_jwt.settings import api_settings模块
        # 获取 JWT_PAYLOAD_HANDLER 荷载方法,其实就是内容,返回的是一个函数对象
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        # 编码处理 JWT_ENCODE_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        # 上面两个方法获取到之后就可以进行操作了,第一步:将用户对象传递进去,获取到该对象的属性值
        payload = jwt_payload_handler(user)
        # 将属性编码成jwt格式的字符串,其实就是生成token
        token = jwt_encode_handler(payload)
        return JsonResponse({'code': 200, 'token': token})

# 定义LoginView类
class LoginView(View):

    # 使用get或者post方法都可以
    def post(self, request):
        username = request.GET.get("username")
        password = request.GET.get("password")
        try:
            user = SysUser.objects.get(username=username, password=password)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            # 将用户对象传递进去,获取到该对象的属性值
            payload = jwt_payload_handler(user)
            # 将属性值编码成jwt格式的字符串
            token = jwt_encode_handler(payload)
        except Exception as e:
            print(f'打印异常:{e}')
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
        return JsonResponse({'code': 200, 'token': token, 'info': '登录成功'})

17、修改"user"子模块下的"urls.py"文件内容,添加登录映射关系;

复制代码
from django.urls import path
from user.views import TestView, JwtTestView, LoginView

urlpatterns = [
    path('test', TestView.as_view(), name='test'),  # test测试
    path('jwt_test', JwtTestView.as_view(), name='jwt_test'),  # JWT测试

    path('login', LoginView.as_view(), name='login'),  # 登录
]

18、打开前端 vue 项目,修改"src"目录"views"文件路径下"Login.vue"文件内容;

复制代码
<template>
  <div class="login">

    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">python_admin Django后台管理系统</h3>

      <el-form-item prop="username">

        <el-input
            v-model="loginForm.username"
            type="text"
            size="large"
            auto-complete="off"
            placeholder="账号"
        >
          <template #prefix><svg-icon icon="user" /></template>
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
            v-model="loginForm.password"
            type="password"
            size="large"
            auto-complete="off"
            placeholder="密码"
        >
          <template #prefix><svg-icon icon="password" /></template>
        </el-input>
      </el-form-item>


      <el-checkbox  style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button
            size="large"
            type="primary"
            style="width:100%;"
            @click.prevent="handleLogin"
        >
          <span>登 录</span>

        </el-button>

      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2000-2345 <a href="http://www.baidu.com" target="_blank">python_admin</a> 版权所有.</span>
    </div>
  </div>
</template>

<script setup>
  import {ref} from 'vue'
  import requestUtil from '@/util/request'
  import qs from 'qs'
  import {ElMessage} from 'element-plus'


  const loginForm=ref({
    username:'',
    password:''
  })

  const loginRef=ref(null)

  const loginRules = {
    username: [{required: true, trigger: "blur", message: "请输入您的账号"}],
    password: [{required: true, trigger: "blur", message: "请输入您的密码"}]
  };

  const handleLogin=()=>{
    loginRef.value.validate(async (valid)=>{
      if(valid){
        let result=await requestUtil.post("user/login?"+qs.stringify(loginForm.value))
        console.log(result)
        let data=result.data
        if(data.code==200){
          ElMessage.success(data.info)
          window.sessionStorage.setItem("token",data.token)
        }else{
          ElMessage.error(data.info)
        }
      }else{
        console.log("验证失败")
      }
    })
  }



</script>

<style lang="scss" scoped>
a{
  color:white
}
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;

  .el-input {
    height: 40px;



    input {
      display: inline-block;
      height: 40px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 0px;
  }

}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 40px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 40px;
  padding-left: 12px;
}
</style>

19、重新运行前后端项目,浏览器输入"http://127.0.0.1:8080/#/login",先不输入账号和密码,直接单击"登录"按钮,发现会验证失败且账号密码会直接出现提示;

20、输入账号密码带参数的传入后端,测试发现报错提示 403;

查看详细报错信息,发现这个报错意思是CSRF 验证报错;因为 Django 默认是带表单参数的话会有一个 CSRF 验证,之前是在前端传入一个 CSRF 带有 token 值的,它就不会报错了;但是现在是前后端分离,已经有 JWT token 认证了,所以不需要,直接关闭 CSRF 验证就行了;

21、打开 Django 后端项目,修改项目"python_admin"应用下的"settings.py"配置文件中的"MIDDLEWARE"设置项,删除或者注释掉"'django.middleware.csrf.CsrfViewMiddleware'"即可;

22、再次测试验证,分别输入数据库表没有的数据和已存在的账号密码(例如:账号'python'密码'123456'),能够分别看到成功和失败两种不同的提示信息;

四、DRF框架引入解决序列化问题

后端返回当前登录用户信息到前端,存sessionStorage,用于前端主页显示当前用户信息;所以的话需要从后端登录成功之后获取返回到前端,然后存储sessionStorage,其他页面需要的话可直接从sessionStorage 这里面获取就行了;这里面会涉及到一个问题,要返回当前的一个对象;

后端 Django5 项目

23、打开 Django 后端项目,修改"user"子模块下的"views.py"文件中 LoginView 类里面的内容,当执行成功的时候,返回当前用户对象;

复制代码
class LoginView(View):

    # 使用get或者post方法都可以
    def post(self, request):
        username = request.GET.get("username")
        password = request.GET.get("password")
        try:
            user = SysUser.objects.get(username=username, password=password)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            # 将用户对象传递进去,获取到该对象的属性值
            payload = jwt_payload_handler(user)
            # 将属性值编码成jwt格式的字符串
            token = jwt_encode_handler(payload)
        except Exception as e:
            print(f'打印异常:{e}')
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
        return JsonResponse({'code': 200, 'token': token, 'user': user, 'info': '登录成功'})

24、启动前后端项目,浏览器输入"http://127.0.0.1:8080/#/login",输入数据库表中存在的正确的账号密码(超级管理员账号 python 密码 123456),单击"登录"按钮,测试发现,如果直接返回"user"用户对象的话,会报错提示 SysUser 类型的对象不是 JSON 序列化对象,因此需要把 SysUser 类型的对象转化成 JSON 序列化对象;对象转成序列化可以遍历对象的源信息,然后一个一个封装再转换成 JSON 序列化对象,这种方式比较麻烦;最简单的方式就是通过 DRF 框架的序列化模块功能来实现;

25、修改"user"子模块下的"models.py"文件内容,定义实现了一个序列化的用户类,使用时只需要把用户对象给传进去就行了;定义 SysUserserializer 对应的一个序列化类(定义一个实体对象对应的序列化对象),继承 DRF 框架序列化serializers.ModelSerializer 基类(导包引入from rest_framework import serializers);

复制代码
from django.db import models
from rest_framework import serializers


# Create your models here.

class SysUser(models.Model):
    id = models.AutoField(primary_key=True)
    username = models.CharField(max_length=100, unique=True, verbose_name='用户名')
    password = models.CharField(max_length=100, verbose_name='密码')
    avatar = models.CharField(max_length=255, null=True, verbose_name='用户头像')
    email = models.CharField(max_length=100, null=True, verbose_name='用户邮箱')
    phonenumber = models.CharField(max_length=11, null=True, verbose_name='手机号码')
    login_date = models.DateField(null=True, verbose_name='最后登录时间')
    status = models.IntegerField(null=True, verbose_name='账号状态(0正常 1停用)')
    create_time = models.DateField(null=True, verbose_name='创建时间')
    update_time = models.DateField(null=True, verbose_name='更新时间')
    remark = models.CharField(max_length=500, null=True, verbose_name='备注')

    class Meta:
        db_table = 'sys_user'

# 定义SysUserserializer对应的一个序列化类,继承DRF框架序列化serializers.ModelSerializer(导包引入from rest_framework import serializers)
class SysUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = SysUser     # 指定model类
        fields = '__all__'

26、修改"user"子模块下的"views.py"文件中 LoginView 类里面的内容,导包引入from user.models import SysUserSerializer 模块,使用 SysUserSerializer(user).data 调用返回后的数据,data 属性就直接能返回对象序列化后的数据;

复制代码
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from rest_framework_jwt.settings import api_settings

from user.models import SysUser, SysUserSerializer


# Create your views here.

# 定义TestView类(导包引入from django.views import View)
class TestView(View):

    def get(self, request):
        # 获取token,需注意在前端是叫"AUTHORIZATION"这个名字,而到Django中则会自动添加"HTTP_"变为"HTTP_AUTHORIZATION"
        token = request.META.get('HTTP_AUTHORIZATION')
        print(f'打印查看token值: {token}')
        # 判断token不等于None值且不为空值,就表示通过了
        if token != None and token != '':
            # 导包引入from user.models import SysUser,通过objects.all()查询所有用户信息
            userList_obj = SysUser.objects.all()
            print(f'打印查看userList_obj类型:{type(userList_obj)},查看: {userList_obj},')
            # 转存字典,里面的对象转换为了字典类型
            userList_dict = userList_obj.values()
            print(f'打印查看userList_dict2类型: {type(userList_dict)},查看:{userList_dict}')
            # 把外层的容器转为List
            userList = list(userList_dict)
            print(f'打印查看userList类型: {type(userList)},查看:{userList}')
            # 导包引入from django.http import JsonResponse
            return JsonResponse({'code': 200, 'info': '测试', 'data': userList})
        else:
            # 没有通过的话就给前端返回401
            return JsonResponse({'code': 401, 'info': '没有测试访问权限!'})

# 定义一个 JwtTestView 测试类,模拟登录
class JwtTestView(View):

    # get请求方式
    def get(self, request):
        # 这里模拟登录是直接从之前添加测试数据的表中获取,返回的是一个用户user
        user = SysUser.objects.get(username='jack', password='123456')
        # 导包引入from rest_framework_jwt.settings import api_settings模块
        # 获取 JWT_PAYLOAD_HANDLER 荷载方法,其实就是内容,返回的是一个函数对象
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        # 编码处理 JWT_ENCODE_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        # 上面两个方法获取到之后就可以进行操作了,第一步:将用户对象传递进去,获取到该对象的属性值
        payload = jwt_payload_handler(user)
        # 将属性编码成jwt格式的字符串,其实就是生成token
        token = jwt_encode_handler(payload)
        return JsonResponse({'code': 200, 'token': token})

# 定义LoginView类
class LoginView(View):

    # 使用get或者post方法都可以
    def post(self, request):
        username = request.GET.get("username")
        password = request.GET.get("password")
        try:
            user = SysUser.objects.get(username=username, password=password)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            # 将用户对象传递进去,获取到该对象的属性值
            payload = jwt_payload_handler(user)
            # 将属性值编码成jwt格式的字符串
            token = jwt_encode_handler(payload)
        except Exception as e:
            print(f'打印异常:{e}')
            return JsonResponse({'code': 500, 'info': '用户名或者密码错误!'})
        return JsonResponse({'code': 200, 'token': token, 'user': SysUserSerializer(user).data, 'info': '登录成功'})

27、浏览器输入"http://127.0.0.1:8080/#/login",输入数据库表中存在的正确的账号密码(超级管理员账号 python 密码 123456),单击"登录"按钮,测试发现,已经能够返回所需要的序列化用户信息;

前端 vue3 项目

前端得获取用户信息,然后存到sessionStorage 值里面去,因为 data.user 是 JS 对象类型,存 sessionStorage 需要转成字符串类型,用JSON.stringify() 转存;

28、打开前端 vue 项目,修改项目"src"目录"views"文件路径下"Login.vue"文件内容;

复制代码
<template>
  <div class="login">

    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">python_admin Django后台管理系统</h3>

      <el-form-item prop="username">

        <el-input
            v-model="loginForm.username"
            type="text"
            size="large"
            auto-complete="off"
            placeholder="账号"
        >
          <template #prefix><svg-icon icon="user" /></template>
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
            v-model="loginForm.password"
            type="password"
            size="large"
            auto-complete="off"
            placeholder="密码"
        >
          <template #prefix><svg-icon icon="password" /></template>
        </el-input>
      </el-form-item>


      <el-checkbox  style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button
            size="large"
            type="primary"
            style="width:100%;"
            @click.prevent="handleLogin"
        >
          <span>登 录</span>

        </el-button>

      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2000-2345 <a href="http://www.baidu.com" target="_blank">python_admin</a> 版权所有.</span>
    </div>
  </div>
</template>

<script setup>
  import {ref} from 'vue'
  import requestUtil from '@/util/request'
  import qs from 'qs'
  import {ElMessage} from 'element-plus'


  const loginForm=ref({
    username:'',
    password:''
  })

  const loginRef=ref(null)

  const loginRules = {
    username: [{required: true, trigger: "blur", message: "请输入您的账号"}],
    password: [{required: true, trigger: "blur", message: "请输入您的密码"}]
  };

  const handleLogin=()=>{
    loginRef.value.validate(async (valid)=>{
      if(valid){
        let result=await requestUtil.post("user/login?"+qs.stringify(loginForm.value))
        console.log(result)
        let data=result.data
        if(data.code==200){
          ElMessage.success(data.info)
          window.sessionStorage.setItem("token",data.token)
          // 这样是不行的,data.user其实是一个JSON对象,根本不是一个数据,它存的是一个对象,而需要的是一个json串
          // window.sessionStorage.setItem("currentUser", data.user)
          // 使用JSON.stringify(data.user)方法把JSON对象转成JSON串;对应的JSON.parse()是把JSON串转成JSON对象
          window.sessionStorage.setItem("currentUser", JSON.stringify(data.user))
        }else{
          ElMessage.error(data.info)
        }
      }else{
        console.log("验证失败")
      }
    })
  }



</script>

<style lang="scss" scoped>
a{
  color:white
}
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;

  .el-input {
    height: 40px;



    input {
      display: inline-block;
      height: 40px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 0px;
  }

}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 40px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 40px;
  padding-left: 12px;
}
</style>

29、浏览器输入"http://127.0.0.1:8080/#/login",输入数据库表中存在的正确的账号密码(超级管理员账号 python 密码 123456),单击"登录"按钮,测试发现,已经能够存为一个 JSON 串;

五、记住密码功能实现

记住密码功能,一般是通过cookie来实现,前端cookie 实现比较方便快捷,首先需要安装依赖 'js-cookie',以及存储用户密码,为了安全起见,密码存储在前端时需要进行加密处理,不然的话会很容易就被看到密码了,所以还需要安装一个叫'jsencrypt'的依赖;

30、打开前端 vue 项目,在项目"src"目录"util"目录文件路径下新建一个名称为"jsencrypt.js"的 JS 文件,输入以下内容,新建一个工具类,用于加密解密;首先是导入 JSEncrypt 库,然后有一个公钥和一个私钥,里面还有两个方法,一个是加密用的,另一个是解密用的;在存储密码的时候需要加密操作,然后cookie 获取的时候需要解密操作,存的时候用公钥,解密的时候使用私钥;

复制代码
// import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
import JSEncrypt from 'jsencrypt'

// 密钥对生成 http://web.chacuo.net/netrsakeypair

const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
    'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='

const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
    '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
    'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
    'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
    'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
    'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
    'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
    'UP8iWi1Qw0Y='

// 加密
export function encrypt(txt) {
    const encryptor = new JSEncrypt()
    encryptor.setPublicKey(publicKey) // 设置公钥
    return encryptor.encrypt(txt) // 对数据进行加密
}

// 解密
export function decrypt(txt) {
    const encryptor = new JSEncrypt()
    encryptor.setPrivateKey(privateKey) // 设置私钥
    return encryptor.decrypt(txt) // 对数据进行解密
}

31、打开项目"src"目录"views"文件路径下"Login.vue"文件,输入以下内容;

复制代码
<template>
  <div class="login">

    <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">python_admin Django后台管理系统</h3>

      <el-form-item prop="username">

        <el-input
            v-model="loginForm.username"
            type="text"
            size="large"
            auto-complete="off"
            placeholder="账号"
        >
          <template #prefix><svg-icon icon="user" /></template>
        </el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
            v-model="loginForm.password"
            type="password"
            size="large"
            auto-complete="off"
            placeholder="密码"
        >
          <template #prefix><svg-icon icon="password" /></template>
        </el-input>
      </el-form-item>


      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
      <el-form-item style="width:100%;">
        <el-button
            size="large"
            type="primary"
            style="width:100%;"
            @click.prevent="handleLogin"
        >
          <span>登 录</span>

        </el-button>

      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2000-2345 <a href="http://www.baidu.com" target="_blank">python_admin</a> 版权所有.</span>
    </div>
  </div>
</template>

<script setup>
  import {ref} from 'vue'
  import requestUtil from '@/util/request'
  import qs from 'qs'
  import {ElMessage} from 'element-plus'
  import Cookies from "js-cookie";
  import { encrypt, decrypt } from "@/util/jsencrypt";


  const loginForm=ref({
    username:'',
    password:'',
    rememberMe:false
  })

  const loginRef=ref(null)

  const loginRules = {
    username: [{required: true, trigger: "blur", message: "请输入您的账号"}],
    password: [{required: true, trigger: "blur", message: "请输入您的密码"}]
  };

  const handleLogin=()=>{
    loginRef.value.validate(async (valid)=>{
      if(valid){
        let result=await requestUtil.post("user/login?"+qs.stringify(loginForm.value))
        console.log(result)
        let data=result.data
        if(data.code==200){
          ElMessage.success(data.info)
          window.sessionStorage.setItem("token",data.token)
          // 这样是不行的,data.user其实是一个JSON对象,根本不是一个数据,它存的是一个对象,而需要的是一个json串
          // window.sessionStorage.setItem("currentUser", data.user)
          // 使用JSON.stringify(data.user)方法把JSON对象转成JSON串;对应的JSON.parse()是把JSON串转成JSON对象
          window.sessionStorage.setItem("currentUser",JSON.stringify(data.user))
          // 勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
          if (loginForm.value.rememberMe) {
            // expires: 30	有效期设置30天:30s--30秒,30m--30分钟,24h--24小时
            Cookies.set("username", loginForm.value.username, { expires: 30 });
            Cookies.set("password", encrypt(loginForm.value.password), { expires: 30 });
            Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
          }else{
            // 否则移除
            Cookies.remove("username");
            Cookies.remove("password");
            Cookies.remove("rememberMe");
          }
        }else{
          ElMessage.error(data.info)
        }
      }else{
        console.log("验证失败")
      }
    })
  }

  function getCookie() {
    const username = Cookies.get("username");
    const password = Cookies.get("password");
    const rememberMe = Cookies.get("rememberMe");
    loginForm.value = {
      username: username === undefined ? loginForm.value.username : username,
      password: password === undefined ? loginForm.value.password : decrypt(password),
      rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
    };
  }

  getCookie();


</script>

<style lang="scss" scoped>
a{
  color:white
}
.login {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  background-image: url("../assets/images/login-background.jpg");
  background-size: cover;
}
.title {
  margin: 0px auto 30px auto;
  text-align: center;
  color: #707070;
}

.login-form {
  border-radius: 6px;
  background: #ffffff;
  width: 400px;
  padding: 25px 25px 5px 25px;

  .el-input {
    height: 40px;



    input {
      display: inline-block;
      height: 40px;
    }
  }
  .input-icon {
    height: 39px;
    width: 14px;
    margin-left: 0px;
  }

}
.login-tip {
  font-size: 13px;
  text-align: center;
  color: #bfbfbf;
}
.login-code {
  width: 33%;
  height: 40px;
  float: right;
  img {
    cursor: pointer;
    vertical-align: middle;
  }
}
.el-login-footer {
  height: 40px;
  line-height: 40px;
  position: fixed;
  bottom: 0;
  width: 100%;
  text-align: center;
  color: #fff;
  font-family: Arial;
  font-size: 12px;
  letter-spacing: 1px;
}
.login-code-img {
  height: 40px;
  padding-left: 12px;
}
</style>

32、浏览器输入"http://127.0.0.1:8080/#/login"打开登录页面,输入数据库表中存在的正确的账号密码(超级管理员账号 python 密码 123456),勾选"记住密码"选项,单击"登录"按钮,登录成功后可以发现在前端浏览器cookie中已经记录了"账户username、密码password、记住密码rememberMe"这三个字段信息;因为现在cookie中已经存在这几个数据信息了,再次刷新或者重新打开登录页面时会回显账户密码且记住密码选项也已经被勾选上了,表示下次登录时会自动的取到账号密码;如果取消勾选"记住密码"选项,单击登录按钮,登录成功后再次刷新页面会发现没有回显账号密码,因为cookie 中已经删掉了这几个字段信息,没有数据,所以此时 cookie 是什么都不会设置的,都是空;

六、自定义中间件实现token鉴权

在实际开发过程中,后端研发会开发出很多的接口,如果需要带 token 鉴权的话,每个接口都去判断 token,都写一段判断 token 的代码,这样的话冗余代码就太多了,而且都是重复的;所以可以利用自定义中间件来统一实现所有接口的鉴权功能,这样的话就会非常的方便,不需要和业务代码参杂在一起;

33、打开后端 Django 项目,在"user"子模块(也可以在其他模块,一般情况都是 user 模块)下新建一个名称为"middleware"的 Python 文件,在"middleware.py"文件里面新建一个 JWT 认证中间类 JwtAuthenticationMiddleware,务必继承 MiddlewareMixin;

复制代码
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
from jwt import ExpiredSignatureError, InvalidTokenError, PyJWTError
from rest_framework_jwt.settings import api_settings


# 定义JwtAuthenticationMiddleware中间类,必须继承MiddlewareMixin(导包from django.utils.deprecation import MiddlewareMixin)
class JwtAuthenticationMiddleware(MiddlewareMixin):

    # 实现一个方法process_request,它是在请求之前进行处理
    def process_request(self, request):
        # 有些请求不需要token认证,例如:登录请求第一次登录是没有token的,验证码请求也不需要token认证
        white_list = ["/user/login"]  # 请求白名单
        path = request.path     # 获取请求路径
        # if判断如果不在白名单里面,且不是"/media"开头的,后期会有一些媒体文件、图片、静态文件会放在media下面
        if path not in white_list and not path.startswith("/media"):
            print("要进行token验证")
            token = request.META.get('HTTP_AUTHORIZATION')  # 获取token
            print("打印输出获取到的token值为:", token)
            try:
                # 然后要解析验证token
                jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
                jwt_decode_handler(token)
            except ExpiredSignatureError:
                return HttpResponse('Token过期,请重新登录!')
            except InvalidTokenError:
                return HttpResponse('Token验证失败!')
            except PyJWTError:
                return HttpResponse('验证过程中Token验证异常!')
        else:
            print("不需要token验证")
            return None     # 可以不写,如果写上return None则比较规范
添加自定义中间件

34、打开项目"python_admin"应用"settings.py"配置文件,修改"MIDDLEWARE"配置项内容,添加"'user.middleware.JwtAuthenticationMiddleware'"设置项位于放置最后一项(最下面);

复制代码
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'user.middleware.JwtAuthenticationMiddleware',
]

35、测试验证,使用之前的接口测试工具,可以发现不带 token 时会验证失败,如果带有准确的 token 值时,直接就放行了,会验证成功。

未完待续......

相关推荐
鬼手点金1 小时前
缓存作为架构约束
开发语言·前端·人工智能·python·microsoft·缓存·架构
月夏1 小时前
前端第一次接自己的后端:mock 切真接口踩了 2 个坑,组件只改了一行 import
前端·后端·node.js
IT_陈寒1 小时前
React实战技巧总结:如何优雅处理复杂列表的渲染卡顿问题?
前端·人工智能·后端
恩喜玛生物2 小时前
AIDD系列02:ProteinMPNN序列设计,从已知骨架到可检验的肽候选
python·ai
FelixZhang0282 小时前
不懂金融或不会编程,如何开始量化?从策略搭建到回测验证
人工智能·python·算法·数学建模·均值算法·自然语言处理·金融
打工仔折腾 AI2 小时前
后端部署避坑:吃透IPv4地址、网络接口与CIDR前缀的实际应用
arm开发·人工智能·后端·python·性能优化
独泪了无痕10 小时前
Spring Data Redis 快速上手
spring boot·redis·后端
wangchunyu11412 小时前
JAVA CompletableFuture异步编程实战
java·开发语言·python
2501_9445215914 小时前
Immer 跨端三方库仓颉适配全流程
python·eclipse·django