后台管理员登录实现--系统篇

news2024/11/30 10:43:20

        我的小系统后台原来就有一个上传图片的功能还夹带个删除图片的功能,还嵌到了一个菜单里面。之前效果如下

        那么现在为了加大安全力度,想增加一个登录页面。通过登录再到这个页面。看着貌似很简单,但是听我细细说来,要新增些什么东西才能做到增加一个登录页面。

        可以先从前端代码入手,也可以从后端代码入手,这边先从前端代码入手,先做出个感性认知,效果如下

        这里选择用Form表单来实现,代码如下

<el-form :model="form" style="max-width: 600px" label-width="60px" label-position="left">
          <el-form-item label="用户名">
            <el-input v-model="form.name" />
          </el-form-item>
          <el-form-item label="密码">
            <el-input v-model="form.password" type="password" />
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit">登录</el-button>
          </el-form-item>
        </el-form>

        细看代码,总共摆放了2个输入框和对应的文字提示以及一个登录按钮。label部分宽度调到了60px,label-width="60px"。label位置调成左对齐,label-position="left"。密码输入框设置成密文模式type="password"

        然后要设置成差不多居中显示和弄一个开关来控制是否渲染这个登录页面,因为之前就说了还有另外一些页面要衔接起来。做法还可以由后端来提供路由控制,就是后端来提供登录页面的路由和登录后主体页面的路由。但是如果是这么做就有很多麻烦的事情,比如打包的时候要打包2份给后端,还要弄2个前端入口,调试不方便等等。

        <div v-if="exist.login_exist === false">
        <router-view></router-view>
    </div>
    <div class="container" v-else>

把上面的表单放到这里

    </div>

        exist.login_exist,这个就是页面开关,为false的时候就显示登录页面,登录成功后可以设置为true,至于差不多居中显示代码如下

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 70vh; /* 使用视窗高度来使容器撑满整个屏幕 */
}

        #app的样式是默认的,我没改过,主要看.container样式

        接下来看看这个入口页面的逻辑实现,没改的话就是App.vue

const router = useRouter()

// do not use same name with ref
const form = reactive({
  name: '',
  password:'',
})

const exist = reactive({login_exist:true})

const onSubmit = () => {
  
  axios.post('http://admin.am8.com/anonym/login', {
      name: form.name,
      password: form.password
    })
    .then(function (response) {

      if (response.data.code === 1) {
          store.setToken(response.data.data)
          exist.login_exist = false
          router.push({path:'/sidebar'})
      } else {
          ElMessage.warning(response.data.msg)
      }
    })
    .catch(function (error) {
      console.log(error);
    });

}

        前面那3个定义分别是定义路由,定义form表单数据对象,定义页面开关

const router = useRouter()

// do not use same name with ref
const form = reactive({
  name: '',
  password:'',
})

const exist = reactive({login_exist:true})

        最后就是登录方法onSubmit的实现,向后端请求登录,注意看登录成功后做的事情

 if (response.data.code === 1) {
          store.setToken(response.data.data)
          exist.login_exist = false
          router.push({path:'/sidebar'})
      }

        存储登录令牌

store.setToken(response.data.data)

        页面开关设置为false

exist.login_exist = false

        跳转到/sidebar

router.push({path:'/sidebar'})

        看不懂/sidebar?那就先看看前端路由部分

import { createRouter, createWebHistory } from 'vue-router'
import Upload1 from './views/Upload1.vue'
import Sidebar from './views/menu/Sidebar.vue'
import App from './App.vue'

export const router = createRouter({
  history: createWebHistory(),
  routes: [
      { path: '/', component: App },
      { path: '/sidebar', component: Sidebar, children: [
          {
          
            path: '',
            component: Upload1,
          },
          {

            path: 'addImage',
            component: Upload1,
          },
        ], },
    ]
})
        看出来了吧,/sidebar是Sidebar.vue这个页面,Sidebar.vue页面仅仅是个我之前写的Menu菜单页面,Menu菜单里面的

        接下来看看后端的实现,数据库基于mysql5.7。先建立2张数据表,一张管理员表

CREATE TABLE `am8_admin` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

        一张管理员登录令牌表

CREATE TABLE `am8_admin_token` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `admin_id` int(11) NOT NULL,
  `token` varchar(100) NOT NULL COMMENT '登录令牌',
  `client` tinyint(3) NOT NULL COMMENT '登录渠道。1.PC,2.H5',
  `create_time` int(11) NOT NULL COMMENT '首次登录时间戳',
  `update_time` int(11) NOT NULL COMMENT '最后一次登录时间戳',
  `ip` varchar(30) NOT NULL COMMENT '登录地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

        后端框架基于thinkphp8,然后新建2个模型类,Admin和AdminToken。新建Anonym控制器,将BaseController复制一份到Admin应用下

        BaseController修改如下

protected function initialize()
    {
        $exist = AdminToken::where([
            'token' => request()->param('token'),
        ])->find();

        if ($exist !== null) {
            // 获取当前时间的时间戳
            $now = time();
            
            $timestamp = intval($exist->update_time);
            
            // 设定1小时的秒数
            $oneHourInSeconds = 3600;
            
            if (($now - $timestamp) > $oneHourInSeconds) {
                return myFailResponse(0, '登录状态已过期');
            }
        } else {
            return myFailResponse(0, '未登录');
        }
    }

        这里实现的是登录状态检查,后面所有需要登录才实现的功能都要继承这个类

        Anonym这个类里面实现了登录的接口,这个类就不用继承BaseController

public function login()
    {
        $admin = new Admin();
        
        $exist = $admin->where([
            'name' => request()->param('name'),
            'password' => $this->encrypt(request()->param('password')),
        ])->find();
        if ($exist !== null) {
            $result = $this->loginStateHandler($exist['id']);
            return mySuccessResponse($result);
        } else {
            return myFailResponse(1, '用户名或密码有误');
        }
        
    }

        encrypt这个是密码的加密方法,这里就不展示了,我也没想好怎么写,只是随手实现了加密。有的人想用hash就用hash,有的人想加盐就加盐,这里不展开了。

        loginStateHandler这个方法是对登录状态做处理。

private function loginStateHandler($id)
    {
        $exist = AdminToken::where([
            'admin_id' => $id,
            'client' => 1
        ])->find();
        
        // 获取当前时间的时间戳
        $now = time();
        
        if ($exist !== null) {
            $timestamp = intval($exist->update_time);
            
            // 设定1小时的秒数
            $oneHourInSeconds = 3600;
            
            if (($now - $timestamp) > $oneHourInSeconds) {
                $token = $this->createToken($id, $now);
                $exist->token = $token;
                $exist->ip = $this->getClientIp();
                $exist->save();
                
                return $token;
            } else {
                $exist->ip = $this->getClientIp();
                $exist->save();
                return $exist['token'];
            }
        } else {
            $admin_token = new AdminToken();
            $token = $this->createToken($id, $now);
            $admin_token->admin_id = $id;
            $admin_token->token = $token;
            $admin_token->client = 1;
            $admin_token->ip = $this->getClientIp();
            $admin_token->save();
            
            return $token;
        }
        
    }

        createToken方法是生成登录令牌的方法,这里就不展开了,因为我没想好,只是随手实现了一下,getClientIp这个是获取客户端ip的方法,也是没想好怎么写,不展开了,随手实现了一个。

        另外还要注意修改配置database.php,datetime_format的值改为false,避免取出时间戳时会被格式化从而导致登录逻辑出错

        既然后端已经支持登录验证,那么前面写的上传图片和删除图片的前端部分也要作出修改,提供相应支持,其实就是增加发送参数token,Upload1.vue文件修改如下

        <el-upload
      v-model:file-list="fileList"
      class="upload-demo"
      action="http://admin.am8.com/index/upload"
      :data="{token:store.token}"
      name="image"
      list-type="picture"
      :on-success="handleSuccess"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :before-remove="beforeRemove"
      :limit="3"
      :on-exceed="handleExceed"
    >

        上传组件增加:data,除了发送上传图片外,多发送个token。

const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile, uploadFiles) => {
  if (response.code === 1) {
      fileList.value.pop()
      fileList.value.push(response.data)
  } else {
      if (response.code === 0) {
        fileList.value.pop()
          ElMessage.warning(response.msg)
      }
  }
  
}

        成功上传后,增加根据后端返回登录状态的提示

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {

  axios.post('http://admin.am8.com/index/deleteFile', {
      token: store.token,
      id: file.id,
    })
    .then(function (response) {
      if (response.data.code === 1) {
          
      } else {
          if (response.data.sub_code === 0) {
              router.push({path:'/'})
          }

      }
    })
    .catch(function (error) {
      console.log(error);
    });
}

        删除图片时也要增加发送token参数,还有登录状态不存在或者失效时跳转回登录页面,前后展示了2种失败后处理做法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218294.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenLayers:构建现代Web地图应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 OpenLayers&#xff1a;构建现代Web地图应用 文章目录 OpenLayers&#xff1a;构建现代Web地图应用1. 简介2. 为什么选择 OpenLa…

Redis 高可用:从主从到集群的全面解析

目录 一、主从复制 (基础)1. 同步复制a. 全量数据同步b. 增量数据同步c. 可能带来的数据不一致 2. 环形缓冲区a. 动态调整槽位 3. runid4. 主从复制解决单点故障a. 单点故障b. 可用性问题 5. 注意事项a. Replica 主动向 Master 建立连接b. Replica 主动向 Master 拉取数据 二、…

腾讯云宝塔面板前后端项目发版

后端发版 1. 打开“网站”页面&#xff0c;找到java项目&#xff0c;点击状态暂停服务 2.打开“文件”页面&#xff0c;进入jar包目录&#xff0c;删除原有的jar包&#xff0c;上传新jar包 3. 再回到第一步中的网站页面&#xff0c;找到jar项目&#xff0c;启动项目即可 前端发…

C#的小数位保留以及四舍五入

C#使用Math.Round("数值","保留位","保留方式")进行小数位保留以及四舍五入 //1.MidpointRounding.ToEven(四舍六入五成双) //当保留小数位后一位为0~4时&#xff0c;舍去末位 var x1 Math.Round(1.124, 2, MidpointRo…

立仪科技:光谱共焦传感器精准测量玻璃

光谱共焦测量技术作为一种创新的光学检测方法&#xff0c;近年来在工业领域引起了广泛关注。 它以其高精度、非接触式的特点&#xff0c;特别适用于透明或半透明材料如玻璃的厚度和表面形貌测量。 接下来&#xff0c;立仪科技小编将深入探讨光谱共焦技术在玻璃测量上的应用及其…

计算机毕业设计Hadoop+Hive+Spark+Flink广告推荐系统 广告预测 广告数据分析可视化 广告爬虫 大数据毕业设计 深度学习 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 专业 小四号宋体 班级 小…

飞机大战告尾

参考 PPO算法逐行代码详解 链接 通过网盘分享的文件&#xff1a;PlaneWar 链接: https://pan.baidu.com/s/1cbLKTcBxL6Aem3WkyDtPzg?pwd1234 提取码: 1234 10.17关于博客发了又改这件事 悲催的事 今天训练了一早上ppo模型&#xff0c;满怀期待的检测成果时发现一点长进都…

mac安装brew时踩坑解决方案

安装包 mac上如果按照git等工具可能会使用brew&#xff0c;例如使用&#xff1a;$ brew install git命令&#xff0c;如果电脑没有按照brew&#xff0c;则会提示&#xff1a;zsh: command not found: brew 解决方案 需要我们打开brew的官网https://brew.sh/&#xff0c;复制…

动态规划一>下降路径最小和

1.题目&#xff1a; 2.解析&#xff1a; 代码&#xff1a; /**1.创建dp表2.初始化3.填表4.返回值*/public int minFallingPathSum(int[][] matrix) {int n matrix.length;int[][] dp new int[n1][n2];int minNum Integer.MAX_VALUE; for(int i 1; i < n; i) dp[i][0]…

【CSS】纯CSS Loading动画组件

<template><div class"ai-loader-box"><!-- AI loader --><div class"ai-loader"><div class"text"><p>AI智能分析中....</p></div><div class"horizontal"><div class&quo…

简单说说 spring是如何实现AOP的(源码分析)

在spring生命周期流程中&#xff0c;有一个过程是执行BeanPostProcessor的后置方法 BeanPostProcessor 是一个接口&#xff0c;其实现有 aop实现的核心类是AbstractAutoProxyCreator&#xff0c;其位于spring-aop包下&#xff0c;实现了BeanPostProcessor //BeanPostProcesso…

【Java小白图文教程】-04-分支结构

本套课程将会从0基础讲解Java语言核心技术&#xff0c;适合人群&#xff1a; 大学中开设了Java语言课程的同学想要专升本或者考研的同学想要考计算机等级证书的同学想要从事Java相关开发开发的同学 精品专题&#xff1a; 01.《C语言从不挂科到高绩点》课程详细笔记 https:/…

transformers 推理 Qwen2.5 等大模型技术细节详解(一)transformers 初始化和对象加载(文末免费送书)

上周收到一位网友的私信&#xff0c;希望老牛同学写一篇有关使用 transformers 框架推理大模型的技术细节的文章。 老牛同学刚开始以为这类的文章网上应该会有很多&#xff0c;于是想着百度几篇质量稍高一点的回复这位网友。结果&#xff0c;老牛同学搜索后发现&#xff0c;类…

力扣61~65题

题61&#xff08;中等&#xff09;&#xff1a; 分析&#xff1a; python代码&#xff1a; # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def rot…

【含开题报告+文档+PPT+源码】基于SpringBoot电脑DIY装机教程网站的设计与实现

开题报告 随着科技的发展和人们对电脑需求的增加&#xff0c;越来越多的人开始自己组装电脑。然而&#xff0c;针对初学者来说&#xff0c;如何选择合适的硬件配置并进行装机是一个相对复杂的过程。随着各种品牌、型号和规格的硬件不断增多&#xff0c;用户需要一个方便快捷的…

Java项目编译不通过,IDEA无法运行或调试Unit test类

mvn test可以通过&#xff0c;但是通过IDEA无法运行或调试&#xff0c;总是弹出一些依赖错误比如&#xff1a; 程序包xxx.xxx.xxx 不存在或找不到符号 解决办法 步骤1&#xff1a;IDEA 打开 File -> Setting ->Compiler &#xff0c;找到“Automatically show first …

20 Shell Script输入与输出

标出输入、标准输出、错误输出 一、程序的基本三个IO流 一&#xff09;文件描述符 ​ 任何程序在Linux系统中都有3个基本的文件描述符 ​ 比如: ​ cd/proc/$$/fd ​ 进入当前shell程序对于内核在文件系统的映射目录中: [rootlocalhost ~]# cd /proc/$$/fd [rootlocalhos…

基于System.js的微前端实现(插件化)

目录​​​​​​​ 写在前面 一、微前端相关知识 &#xff08;一&#xff09;概念 &#xff08;二&#xff09; 优势 &#xff08;三&#xff09; 缺点 &#xff08;四&#xff09;应用场景 &#xff08;五&#xff09;现有框架 1. qiankun 2. single-spa 3. SystemJ…

【MR开发】在Pico设备上接入MRTK3(一)——在Unity工程中导入MRTK3依赖

写在前面的话 在Pico上接入MRTK3&#xff0c;目前已有大佬开源。 https://github.com/Phantomxm2021/PicoMRTK3 也有值得推荐的文章。 MRTK3在PICO4上的使用小结 但由于在MacOS上使用MRTK3&#xff0c;无法通过Mixed Reality Feature Tool工具管理MRTK3安装包。 故记录一下…

Dockerr安装Oracle以及使用DBeaver连接

拉取镜像 pull container-registry.oracle.com/database/free:latest 创建容器 说明一下我现在的最新版本是23 docker run -d --name oracle23i -h xrilang -p 1521:1521 container-registry.oracle.com/database/free:latest 查看日志 docker logs oracle23i 设置密码 因为创建…