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

vue中通过js控制scss变量

<!--* Description:* Author: 李大玄* Date: 2022-07-28 20:34:43* FilePath: /web-framework-demo/src/views/layout.vue* LastEditors: 李大玄* LastEditTime: 2022-11-01 09:25:31 --> <template><div height"100%" class"b"><inp…...

深度学习理论知识入门【EM算法、VAE算法、GAN算法】和【RBM算法、MCMC算法、HMC算法】

目录 深度学习理论知识入门首先&#xff0c;让我们了解第一个流程&#xff1a;现在&#xff0c;让我们看看第二个流程&#xff1a; EM算法GMM&#xff08;高斯混合模型&#xff09; 深度学习理论知识入门 首先&#xff0c;让我们了解第一个流程&#xff1a; EM&#xff08;Exp…...

Java8实战-总结47

Java8实战-总结47 CompletableFuture&#xff1a;组合式异步编程让代码免受阻塞之苦使用定制的执行器 对多个异步任务进行流水线操作 CompletableFuture&#xff1a;组合式异步编程 让代码免受阻塞之苦 使用定制的执行器 就这个主题而言&#xff0c;明智的选择似乎是创建一个…...

功能: 在web应用程序中、读取文件

通过使用文件 API&#xff0c;web 内容可以要求用户选择本地文件&#xff0c;然后读取这些文件的内容。这种选择可以通过使用 HTML <input type"file"> 元素或通过拖放来完成。 1.通过 click() 方法使用隐藏的文件 input 元素 你可以隐藏公认难看的文件 <…...

TDD、BDD、ATDD以及SBE的概念和区别

在软件开发或是软件测试中会遇到以下这些词&#xff1a;TDD 、BDD 、ATDD以及SBE&#xff0c;这些词代表什么意思呢&#xff1f; 它们之间有什么关系吗&#xff1f; TDD 、BDD 、ATDD以及SBE的基本概念 TDD&#xff1a;&#xff08;Test Driven Development&#xff09;是一种…...

Android studio:打开应用程序闪退的问题

目录 问题描述分析原因解决方法 在开发Android应用程序的过程中遇到的问题 问题描述 在开发&#xff08;或者叫测试&#xff0c;这么简单的程序可能很难叫开发&#xff09;好一个android之后&#xff0c;在Android studio中调试开发好的app时&#xff0c;编辑器没有提示错误&a…...

Mysql数据库性能优化--performance_SCHEMA.STATEMENTS语句分析

使用performance_schema解决常见的故障案例 1 检查sql语句 使用performance_schema很容易找到引起性能问题的查询以及原因。 要启动语句检测&#xff0c;需要启动statement类型的插装。 插装类&#xff1a; statement/sql sql语句&#xff0c;如select,或者create table。s…...

[C/C++]数据结构 链表OJ题: 反转链表

描述: 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 示例: 方法一: 让链表指向反向 如图所示: 代码思路: struct ListNode* reverseList(struct ListNode* head) {struct ListNode* n1NULL;struct ListNode* n2head;struct ListNode*…...

深度学习之基于YoloV5交通信号标志识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于YoloV5交通信号标志识别系统介绍 基于YoloV5的交通信号标志识别系统是一种深度学习应用&#xff0c;旨在通过使…...

Linux命令大全

荒诞也好&#xff0c;愚笨也好&#xff0c;总会过去的 文章目录 文件相关压缩相关tarzip 进程相关pskill 网络相关netstat IPC相关ipcsipcrm 系统资源相关topfreefdiskdfdu 权限相关umaskchmodchownchgrp 总结 文件相关 ls&#xff1a;列出当前目录中的文件和子目录。 ls常用…...