当前位置: 首页 > news >正文

【Ruoyi管理后台】用户登录强制修改密码

近期有个需求,就是需要调整Ruoyi管理后台:用户如果三个月(长时间)未修改过密码,需要在登录时强制修改密码,否则不能登录系统。

 一、后端项目调整

从需求来看,我们需要在用户表增加一个字段,用于标记用户最近一次修改密码的时间。

1.调整表结构

1)用户表 sys_user 加入 pwd_time 字段

每次更新密码时,都需要更新为当前系统时间。

2)调整对应的xml映射文件 SysUserMapper.xml

3)调整对应的实体对象 SysUser

同时加入对应的 get/set 方法。

2.调整登录接口

为什么要在登录接口这个地方调整,是因为后面的重置密码接口是需要登录后的 token 才能够调用成功,否则会提示登录已过期。

1)SysLoginService

加入判断用户是否已超过三个月没修改过密码

    public boolean isPwdExpire(String username) {SysUser sysUser = userService.selectUserByUserName(username);Date pwdTime = sysUser.getPwdTime();LocalDate pwdDate = pwdTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();// 获取当前日期LocalDate currentDate = LocalDate.now();// 计算日期差异Period period = Period.between(pwdDate, currentDate);// 检查月份差异是否大于等于三个月if (period.toTotalMonths() >= 3) {return true;}return false;}

2)SysLoginController

在 login 方法内,加入上面的方法判断

    boolean pwdExpire = loginService.isPwdExpire(loginBody.getUsername());if (pwdExpire) {ajax.put("res_code", 1001);String signKey = Constants.RESET_SIGN_KEY + loginBody.getUsername();String signCode = IdUtils.fastSimpleUUID();redisCache.setCacheObject(signKey, signCode, Constants.RESET_EXPIRATION, TimeUnit.MINUTES);ajax.put("reset_sign",signCode);}

这里加入了返回码 res_code ,用于页面判断当前登录用户是否要做重置密码的操作。

同时出于安全的考虑,还使用了一些校验机制,生成一个sign标记,并设置在redis缓存里,用于重置密码时校验。

3.加入重置密码接口

原本在后台管理系统的个人中心是有一个重置密码的地方,对应的也有一个重置密码的接口,但这是在用户已登录能正常进入后台的情况下操作的,跟我们这里的需求不一样,所以我们需要另外写一个接口来处理登录时的密码重置。

1)SysProfileController

    /*** 重置密码*/@Log(title = "个人信息", businessType = BusinessType.UPDATE)@PostMapping("/resetPwd")public AjaxResult resetPwd(@RequestBody ResetBody resetBody){String username = resetBody.getUsername();String sign = resetBody.getSign();String signKey = Constants.RESET_SIGN_KEY + username;String cacheSign = redisCache.getCacheObject(signKey);if (StringUtils.isEmpty(cacheSign)) {return AjaxResult.error("链接已失效");}if (!cacheSign.equals(sign)) {return AjaxResult.error("sign有误");}String code = resetBody.getCode();String uuid = resetBody.getUuid();sysLoginService.validateCaptcha(username, code, uuid, false);SysUser sysUser = userService.selectUserByUserName(username);if (sysUser == null) {return AjaxResult.error("用户不存在");}String oldPassword = resetBody.getOldPassword();String password = sysUser.getPassword();if (!SecurityUtils.matchesPassword(oldPassword, password)) {return AjaxResult.error("修改密码失败,旧密码错误");}String newPassword = resetBody.getNewPassword();if (userService.resetUserPwd(username, SecurityUtils.encryptPassword(newPassword)) > 0){LoginUser loginUser = getLoginUser();// 删除用户缓存记录tokenService.delLoginUser(loginUser.getToken());// 删除缓存redisCache.deleteObject(signKey);// 前端重定向到 login 页面return AjaxResult.success();}return AjaxResult.error("修改密码异常,请联系管理员");}

方法中处理了一些校验,如上面说到的sign标记,还有页面上的图形验证码等,增加安全性。

重置密码生成后,需要清除用户缓存记录,用于使用户必须重新登录。

2)ResetBody

接收重置密码的请求参数。

public class ResetBody {/*** 用户名*/private String username;private String oldPassword;private String newPassword;private String confirmPassword;private String code;private String sign;private String uuid;//getter / setter ...}

二、前端项目调整

1.调整 src/store/modules/user.js

这里需要调用login接口后的返回信息,带回到登录页面,用于后续的操作。

2.调整 src/api/system/user.js

加入重置密码的接口

// 用户密码重置
export function resetUserProfilePwd(data) {return request({url: '/system/user/profile/resetPwd',method: 'post',data: data})
}

3.加入重置密码页面 src/views/reset.vue

<template><div class="register"><el-form ref="resetForm" :model="resetForm" :rules="resetRules" class="register-form"><h3 class="title">修改密码</h3><el-form-item label="旧密码" prop="oldPassword"><el-input v-model="resetForm.oldPassword" placeholder="请输入旧密码" type="password" show-password/></el-form-item><el-form-item label="新密码" prop="newPassword"><el-input v-model="resetForm.newPassword" placeholder="12位由数字、大小写字母、符号组成" type="password" show-password/></el-form-item><el-form-item label="确认密码" prop="confirmPassword"><el-input v-model="resetForm.confirmPassword" placeholder="请确认密码" type="password" show-password/></el-form-item><el-form-item prop="code" v-if="captchaOnOff"><el-inputv-model="resetForm.code"auto-complete="off"placeholder="验证码"style="width: 63%"><svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /></el-input><div class="register-code"><img :src="codeUrl" @click="getCode" class="register-code-img"/></div></el-form-item><el-form-item style="width:100%;"><el-button:loading="loading"size="medium"type="primary"style="width:100%;"@click.native.prevent="handleReset"><span v-if="!loading">确认修改</span></el-button></el-form-item></el-form><!--  底部  --><div class="el-register-footer"><span>Copyright © 2018-2021 ruoyi.vip All Rights Reserved.</span></div></div>
</template><script>
import { getCodeImg } from "@/api/login";
import { resetUserProfilePwd } from "@/api/system/user";
import { setToken } from '@/utils/auth'export default {name: "Reset",data() {const equalToPassword = (rule, value, callback) => {if (this.resetForm.newPassword !== value) {callback(new Error("两次输入的密码不一致"));} else {const reg = /^(?![A-z0-9]+$)(?![A-z~!@#$%^&*()_+]+$)(?![0-9~!@#$%^&*()_+]+$)([A-z0-9~!@#$%^&*()_+]{12,})/gif (!reg.test(value)) {callback(new Error("输入的密码必须包含数字、大小写字母、符号"));}callback();}};return {codeUrl: "",resetForm: {username: "",oldPassword: "",newPassword: "",confirmPassword: "",code: "",sign: "",uuid: ""},resetRules: {oldPassword: [{ required: true, trigger: "blur", message: "旧密码不能为空" },],newPassword: [{ required: true, message: "新密码不能为空", trigger: "blur" },{ min: 12, max: 20, message: "长度在 12 到 20 个字符", trigger: "blur" }],confirmPassword: [{ required: true, message: "确认密码不能为空", trigger: "blur" },{ required: true, validator: equalToPassword, trigger: "blur" }],code: [{ required: true, trigger: "change", message: "请输入验证码" }]},loading: false,captchaOnOff: true};},created() {this.getCode();},mounted() {// 先清除token,防止回退后能直接登录,从而绕过强制重置密码的逻辑setToken('');// 获取当前链接的参数const params = this.$route.query;this.resetForm.sign = params.sign;this.resetForm.username = params.username;this.token = params.token;},methods: {getCode() {getCodeImg().then(res => {this.captchaOnOff = res.captchaOnOff === undefined ? true : res.captchaOnOff;if (this.captchaOnOff) {this.codeUrl = "data:image/gif;base64," + res.img;this.resetForm.uuid = res.uuid;}});},handleReset() {this.$refs.resetForm.validate(valid => {if (valid) {this.loading = true;// 获取并设置从登录页拿到的token,调用接口需要登录成功的token,否则会提示过期setToken(localStorage.getItem("reset_token"));resetUserProfilePwd(this.resetForm).then(res => {this.$alert("<font color='red'>修改成功,请重新登录</font>", '系统提示', {dangerouslyUseHTMLString: true,type: 'success'}).then(() => {//删除登录成功设置的tokenlocalStorage.removeItem("reset_token");//清除token,强制登录setToken("")// 跳转到登录页this.$router.push("/login");}).catch(() => {});}).catch(() => {this.loading = false;if (this.captchaOnOff) {this.getCode();}})}});}}
};
</script><style rel="stylesheet/scss" lang="scss">
.register {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;
}.register-form {border-radius: 6px;background: #ffffff;width: 400px;padding: 25px 25px 5px 25px;.el-input {height: 38px;input {height: 38px;}}.input-icon {height: 39px;width: 14px;margin-left: 2px;}
}
.register-tip {font-size: 13px;text-align: center;color: #bfbfbf;
}
.register-code {width: 33%;height: 38px;float: right;img {cursor: pointer;vertical-align: middle;}
}
.el-register-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;
}
.register-code-img {height: 38px;
}
</style>

重点在于 handleReset 函数,可参考注释说明。

密码规则为 长度12-20位,必须包含数字、大小写字母、符号。

4.调整 src/router/index.js

 路由加入跳转到重置密码页面。

  {path: '/reset',component: (resolve) => require(['@/views/reset'], resolve),hidden: true}

5.调整 src/views/login.vue

调整 handleLogin 函数,加入判断是否需要重置密码的逻辑。

    this.$store.dispatch("Login", this.loginForm).then((res) => {if (res.res_code && res.res_code === 1001) {// 判断到后端接口返回的重置密码标识码// 先设置tokenlocalStorage.setItem("reset_token", res.token);// 重定向到重置密码页,并带上校验参数this.redirect = '/reset?' + 'sign=' + res.reset_sign + '&username=' + this.loginForm.username;}this.$router.push({ path: this.redirect || "/" }).catch(()=>{});}).catch(() => {this.loading = false;if (this.captchaOnOff) {this.getCode();}});

三、说在最后

这个需求的难点在于,怎么在登录页强制跳转到重置密码页,但是又要防止用户此时返回到 index 地址路径时,不能成功登录到后台,因为这时 token 已经设置了。不过这个 token 也不能随便清掉,因为后面重置密码的接口在调用的时候,就需要用到这个 token。

所以整个需求的难点就围绕着怎么去处理这个登录后的 token,最后选择的方案就是将 token 先保存在 localStorage,这样就可以在跳转到重置密码页的时候先清掉 token,防止用户绕过返回到 index 首页,同时在调用重置密码接口的时候,先从 localStorage 拿回来 token,设置后再调用接口。

相关文章:

【Ruoyi管理后台】用户登录强制修改密码

近期有个需求&#xff0c;就是需要调整Ruoyi管理后台&#xff1a;用户如果三个月(长时间)未修改过密码&#xff0c;需要在登录时强制修改密码&#xff0c;否则不能登录系统。 一、后端项目调整 从需求来看&#xff0c;我们需要在用户表增加一个字段&#xff0c;用于标记用户最…...

计算机网络基础知识1

1、tcp三次握手&#xff1f; SYN&#xff0c;标志位&#xff0c;用于建立TCP连接的握手过程中的标志位。 ACK&#xff0c;确认位&#xff0c;用于说明整个包是确认报文。 TCP/IP协议是传输层的一个面向连接提供可靠安全的传输协议。第一次握手有客户端发起&#xff0c;客户端向…...

人机交互中的多/变尺度态势感知

人机交互是指在人与计算机之间进行信息交换和任务完成的过程中&#xff0c;通过各种界面和交互方式来实现人机之间的有效沟通和协作。多尺度上下文是人机交互中一个重要的概念&#xff0c;它指的是在不同层次或不同尺度的信息之间建立联系&#xff0c;以便更好地理解和处理信息…...

命名管道原理(和匿名管道的对比),mkfifo(命令行,函数),命名管道模拟实现代码+与多个子进程通信代码

目录 命名管道 引入 原理 和匿名管道的对比 使用 -- mkfifo 命令行指令 创建 文件类型p 使用 函数 函数原型 模拟实现 头文件 客户端代码 服务端代码 运行情况 模拟实现 -- 与多个子进程 介绍 服务端代码: 运行情况 命名管道 引入 匿名管道只能用于父子进程…...

pytest全局变量的使用

这里重新阐述下PageObject设计模式&#xff1a; PageObject设计模式是selenium自动化最成熟&#xff0c;最受欢迎的一种模式&#xff0c;这里用pytest同样适用 这里直接提供代码&#xff1a; 全局变量 conftest.py """ conftest.py 全局变量&#xff0c;主要实…...

FreeRTOS源码阅读笔记2--list.c

list.c中主要完成列表数据结构的操作&#xff0c;有列表和列表项的初始化、列表的插入和移除。 2.1列表初始化vListInitialise() 2.1.1函数原型 void vListInitialise( List_t * const pxList ) pxList&#xff1a;列表指针&#xff0c;指向要初始化的列表。 2.1.2函数框架…...

杂货铺 | citespace的使用

安装教程 【CiteSpace保姆级教程1】文献综述怎么写&#xff1f; &#x1f4da;数据下载 1. 新建文件夹 2. 数据下载 知网高级检索 数据选中导出 &#xff1a;一次500 导出后重命名为download_xxx.txt&#xff0c;放到input文件里 3. 数据转换 把output里的数据复制到data里…...

C++ 静态成员变量初始化规则

每一天一个小trick&#xff01;&#xff01; 为什么静态成员不能在类内初始化&#xff1f; 在C中&#xff0c;类的静态成员&#xff08;static member&#xff09;必须在类内声明&#xff0c;在类外初始化&#xff0c;像下面这样。 class A { private: static int count …...

Docker安装、卸载,以及各种操作

docker是一个软件&#xff0c;是一个运行与linux和windows上的软件&#xff0c;用于创建、管理和编排容器&#xff1b;docker平台就是一个软件集装箱化平台&#xff0c;是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xf…...

深入理解 C 语言的内存管理

文章目录 引言内存管理的重要性C语言内存布局C语言内存管理堆和栈内存的区别和用途内存分配和释放的过程C语言动态内存分配的概念和原因malloc()、calloc() 和 realloc() 等函数的使用悬挂指针和野指针内存泄漏和如何避免结论 引言 C语言是充满力量且灵活的编程语言&#xff0…...

利用Caddy实现http反向代理

利用Caddy实现http反向代理 1 Caddy是什么 Caddy是一个开源的&#xff0c;使用Golang编写的&#xff0c;支持HTTP/2的Web服务端。它的一个显著特征就是默认启用HTTPS。 和nginx类似。 2 多个后端服务 假如现在有3个后端http服务&#xff1a;分别在启动在 app1 http://10…...

【Qt之QVariant】使用

介绍 QVariant类类似于最常见的Qt数据类型的联合。由于C禁止联合类型包括具有非默认构造函数或析构函数的类型&#xff0c;大多数有趣的Qt类不能在联合中使用。如果没有QVariant&#xff0c;则QObject::property()和数据库操作等将会受到影响。 QVariant对象同时持有一个单一…...

xv6实验课程--xv6的写时复制fork(2023)

7. xv6实验课程--xv6的写时拷贝(COW)(2021) 7. xv6实验课程--xv6懒惰分页分配&#xff08;lazy)(2020) 本文来源&#xff1a; https://mp.weixin.qq.com/s/XJkhjrlP232ZDsRyXd0oHQ 已完成的实验代码可以从下列网站获取&#xff1a; git clone https://gitee.com/lhwhit196…...

在Windows或Mac上安装并运行LLAMA2

LLAMA2在不同系统上运行的结果 LLAMA2 在windows 上运行的结果 LLAMA2 在Mac上运行的结果 安装Llama2的不同方法 方法一&#xff1a; 编译 llama.cpp 克隆 llama.cpp git clone https://github.com/ggerganov/llama.cpp.git 通过conda 创建或者venv. 下面是通过conda 创建…...

Spring底层原理学习笔记--第七讲--(初始化与销毁)

初始化与销毁 Spring提供了多种初始化和销毁手段它们的执行顺序 A07Application.java package com.lucifer.itheima.a07;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springfram…...

基于斑马算法的无人机航迹规划-附代码

基于斑马算法的无人机航迹规划 文章目录 基于斑马算法的无人机航迹规划1.斑马搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用斑马算法来优化无人机航迹规划。 1.斑马搜索算法 …...

干货 | 接口自动化测试分层设计与实践总结

接口测试三要素&#xff1a; 参数构造 发起请求&#xff0c;获取响应 校验结果 一、原始状态 当我们的用例没有进行分层设计的时候&#xff0c;只能算是一个“苗条式”的脚本。以一个后台创建商品活动的场景为例&#xff0c;大概流程是这样的(默认已经是登录状态下)&#…...

【Linux】服务器与磁盘补充知识,硬raid操作指南

服务器硬件 cpu 主板 内存 硬盘 网卡 电源 raid卡 风扇 远程管理卡 1.硬盘尺寸: 目前生产环境中主流的两种类型硬盘 3.5寸 和2.5寸硬盘 2.5寸硬盘可以通过使用硬盘托架后适用于3.5寸硬盘的服务器 但是3.5寸没法转换成2.5寸 2.如何在服务器上制作raid 华为服务器为例子做…...

【java】实现自定义注解校验——方法二

自定义注解校验的实现步骤&#xff1a; 1.创建注解类&#xff0c;编写校验注解&#xff0c;即类似NotEmpty注解 2.编写自定义校验的逻辑实体类&#xff0c;编写具体的校验逻辑。(这个类可以实现ConstraintValidator这个接口&#xff0c;让注解用来校验) 3.开启使用自定义注解进…...

算法通关村第六关|白银|二叉树的层次遍历【持续更新】

1.二叉树基本的层序遍历 仅仅遍历并输出全部元素。 List<Integer> simpleLevelOrder(TreeNode root) {if (root null) {return new ArrayList<Integer>();}List<Integer> res new ArrayList<Integer>();LinkedList<TreeNode> queue new Lin…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi&#xff0c;还有多少人在用&#xff1f; 去年&#xff0c;月之暗面创始人杨植麟别提有多风光了。90后清华学霸&#xff0c;国产大模型六小虎之一&#xff0c;手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水&#xff0c;单月光是投流就花费2个亿。 疯…...