当前位置: 首页 > 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…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...