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

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

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

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

这里选择用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种失败后处理做法

相关推荐
码界奇点15 分钟前
基于Spring Boot与Vue的校园后台管理系统设计与实现
vue.js·spring boot·后端·毕业设计·源代码管理
Dontla19 分钟前
打开网站时弹出Accept Cookies(接受Cookie)提示是什么意思?(数据保护法规,欧盟GDPR)
前端·数据库
壹号机长1 小时前
canvas烟花特效各种前端框架都可以使用H5,vue,react,
vue.js·react.js·前端框架
咸鱼2.01 小时前
【java入门到放弃】VUE部分知识点
java·javascript·vue.js
weixin_489690021 小时前
MicroSIP自定义web拨打协议
服务器·前端·windows
幻云20101 小时前
Python机器学习:筑基与实践
前端·人工智能·python
web小白成长日记1 小时前
Vue3中如何优雅实现支持多绑定变量和修饰符的双向绑定组件?姜姜好
前端·javascript·vue.js
晴天飛 雪1 小时前
Spring Boot 接口耗时统计
前端·windows·spring boot
0思必得01 小时前
[Web自动化] Selenium模拟用户的常见操作
前端·python·selenium·自动化
Apifox.2 小时前
测试用例越堆越多?用 Apifox 测试套件让自动化回归更易维护
运维·前端·后端·测试工具·单元测试·自动化·测试用例