在原有项目进行业务逻辑开发:同一用户短时间不得提交多次申请,以及更新主表时数据刷新掉了角色权限以及密码重置的问题,详细思路及代码

开发背景:

        用户提交表单后,插入到对应数据库表的字段中去,因需要保存是哪一个用户提交的,所以需要拿到主表的user_id,更新功能为记录提交时间,短时间不得再次提交

        在对一个已有角色权限分配,登录进行基础业务开发的时候,在原有用户表的基础上添加了一个字段来记录时间,前端提交表单后,在后端多表联查时仅需要主键和这个字段的值(并未写sql),调用了MP层的save(密码重置的问题)方法,后续又调用了Update方法(解决角色权限问题,乌龙,因为当时未注意调用的是UserService层写的同名方法,在这耽误了不少的时间

业务开发思路

这一业务需求并不困难,基本思路为:用户提交表单 --> 先拿到登录信息的user_id  --> 根据user_id查找到主表用户的新字段

-->如果字段为空,则保存本地时间 -->保存user_id插入表单信息到对应的表中

-->如果字段不为空,则和当前时间作比较

               --> 当前时间小于字段时间,则提示用户处于冻结时间无法操作 --> 并抛出异常

               --> 当前时间大于字段时间,则重置冻结时间为当前时间+5分钟 --> 保存user_id插入表单信息到对应的表中

业务实际开发过程

1.主表添加冻结字段记录冻结时间

2.用户提交表单后,调用接口

2.1 先拿到登录信息的user_id(光拿user_id或拿Userinfo(包含user_id))

当时考虑的是后续说不定会用到Userinfo里面的字段,所以选择的第二种

2.1.1 只拿user_id

       Controller层继承了一个AbstractController(系统自写)

       在AbstractController中用到的方法

protected SysUserEntity getUser() {return (SysUserEntity) SecurityUtils.getSubject().getPrincipal();}protected Long getUserId() {return getUser().getUserId();}

代码在第二种方式有解释,因为是声明为protected,所以咱们可以直接在Controller层调用

2.1.2 拿Userinfo(包含user_id)

Controller层

        创建VO对象调用service层getLoginUserInfo方法

UserInfoVO userInfoVO = sysUserService.getLoginUserInfo();

在service层中的实现

        创建实体对象:  SecurityUtils是Shiro框架提供的一个工具类,.getSubject()方法返回的是线程绑定的Subject实例,包含了用户的大部分信息(用户名,密码,授权,状态等等),.getPrincipal()是获取主体的主要身份信息,然后将返回的Subjcet对象强转为SysUserEntity对象

        判断登录用户是否为空:调用了Spring框架中的Asset进行条件判断。它确保了从安全上下文中获取到的当前登录用户(loginUser)不为空。如果为空,则抛出一个带有自定义错误消息

        创建VO层对象接收基本信息: 调用Mapper的getUserInfoById(),在dao层写的多表联查sql语句

        填充用户对应的角色信息,因为在user表中并没有直接绑定用户的role,而是通过关联表查询,但这个也不是拿到对应的role_id,而是名称,返回VO对象

public UserInfoVO getLoginUserInfo() {SysUserEntity loginUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();Assert.notNull(loginUser,"获取当前登录人员信息失败!");UserInfoVO userInfoVO = this.baseMapper.getUserInfoById(loginUser.getUserId());// 获取用户对应的角色信息userInfoVO.setRoleNames(sysUserRoleService.queryRoleNameList(loginUser.getUserId().toString()));return userInfoVO;}
2.2 根据User_id查找冻结时间并判断  问题代码(密码失效问题)(权限失效问题)

密码失效

最开始的方法比较草率,导致出了问题,根据user_id先拿到user对象的所有信息,然后set时间,然后调用方法直接保存

SysUserEntity user = sysUserService.getById(getUserId());long nowTimestamp = new Date().getTime();long freezeTimestamp = nowTimestamp + 5 * 60 * 1000;Date freezeUntilDate = new Date(freezeTimestamp);user.setFreezeUntil(freezeUntilDate);System.out.println(user);sysUserService.update(user);

问题出在拿到的user对象将所有的数据取了出来,然而密码是加密的形式,再进行保存便出现了原始密码并不可用的问题,而且其他数据也并不需要,拿整个对象的数据就多此一举了

权限失效

sysUserService.update(user);

其实问题也是出在了这里,当时将密码重置的问题解决后,在第二天发现权限消失了,又回来看这个代码,当时因为在User的实体类中定义了roleIdList的list集合,但是在数据库主表中的并未设置这个字段,而且注解设置的也是false不存在,然后在后面修正的代码测试时拿到的数据也是为空,我就在想会不会是其他的地方用到了这个字段,例如登录的时候对其进行了操作?

然后为了验证是不是这一个方法出了问题,有且仅有可能是这里出了问题,所以又用Warpper去写了一个方法尝试,问题便解决了,但还是想知道为什么会有什么问题

UpdateWrapper<SysUserEntity> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("user_id", userInfoVO.getUserId()).set("freeze_until", newFreezeUntilDate)
sysUserService.update(user, updateWrapper);

MabatisPlus根据后面的代码也不会对空的字段进行操作,然后就去翻了翻对权限分配的一些Service层的方法,结果发现在Service层有个同名的Update方法,在Controller层调用的也是这一层的方法

@TableField(exist=false)private List<Long> roleIdList;

Service层的实现

这是在User修改个人信息的时候调用到的方法

检查密码:对传入的对象中的密码字段进行校验,如果为空,则将密码设为null,这样在更新时不会修改用户的密码。如果不为空,则使用Sha256Hash进行加密(结合了用户盐值user.getSalt()),并将加密后的密码字符串(十六进制形式)重新赋给用户对象的密码字段。 Sha256Hash也是Shiro下的方法

更新用户的基本信息:调用mabtisplus的updateByid方法,根据实体类非空属性更新对应id的数据

因为在修改用户的界面可以修改用户的角色属性,即更改权限,所以在调用saveOrUpdate方法时,后端传回RoleIdList是null值,

public void update(SysUserEntity user) {if(StringUtils.isBlank(user.getPassword())){user.setPassword(null);}else{user.setPassword(new Sha256Hash(user.getPassword(), user.getSalt()).toHex());}this.updateById(user);//检查角色是否越权
//		checkRole(user);//保存用户与角色关系sysUserRoleService.saveOrUpdate(user.getUserId(), user.getRoleIdList());}

在进到aveOrUpdate方法后,我们就可以发现因为null值,所以我在数据库当中的权限也没有了,到这里后便发现了为什么权限消失的问题

public void saveOrUpdate(Long userId, List<Long> roleIdList) {//先删除用户与角色关系this.removeByMap(new MapUtils().put("user_id", userId));if(roleIdList == null || roleIdList.size() == 0){return ;}

 修改后的代码:

1.创建user对象:在bug找到后其实就用service层的对象就ok的。创建了一个UpdateWrapper对象,用于更新数据库中的数据。 再通过Service层的getOne方法通过QueryWrapper对象查询数据库中user_id为用户登录id的记录,只返回user_id,以及冻结时间的字段并只返回一条记录

2.比较时间:先获取当前时间戳nowTImestamp(用于做比较)  -->检查冻结时间是否为空 

-->如果为空,计算新的冻结时间点freezeTimestamp,即当前时间加5分钟(以毫秒为单位),再将时间戳转换为Date对象 freezeUntilDate(为了返回给数据库字段) ,构建UpdateWrapper,调用eq()方法根据用户ID,set()方法更新其在数据库中的'freeze_until'字段为新的冻结结束日期

-->时间不为空,先获取用户现有的冻结时间时间戳,再与当地时间进行比较

        -->大于当地时间戳则抛出异常,提示无法操作

        -->小于当地时间,获取当地时间+5分钟的时间戳对象,并将时间对象调用eq()方法根据用户ID,set()方法更新其在数据库中的'freeze_until'字段为新的冻结结束日期

 //1.UpdateWrapper<SysUserEntity> updateWrapper = new UpdateWrapper<>();//2.SysUserEntity user = sysUserService.getOne(new QueryWrapper<SysUserEntity>().eq("user_id", userInfoVO.getUserId()).select("user_id", "freeze_until").last("limit 1"));//3.long nowTimestamp = new Date().getTime();if (user.getFreezeUntil() == null) {long freezeTimestamp = nowTimestamp + 5 * 60 * 1000;Date freezeUntilDate = new Date(freezeTimestamp);//user.setFreezeUntil(freezeUntilDate);updateWrapper.eq("user_id", userInfoVO.getUserId()).set("freeze_until", freezeUntilDate);} else {long freezeUntilTimestamp = user.getFreezeUntil().getTime();if (nowTimestamp < freezeUntilTimestamp) {throw new IllegalStateException("当前时间小于冻结结束时间,无法操作");} else {// 当前时间已经超过冻结期,则重置冻结时间为当前时间+5分钟long newFreezeTimestamp = nowTimestamp + 5 * 60 * 1000;Date newFreezeUntilDate = new Date(newFreezeTimestamp);// user.setFreezeUntil(newFreezeUntilDate);updateWrapper.eq("user_id", userInfoVO.getUserId()).set("freeze_until", newFreezeUntilDate);}}
2.2 保存对象

1.调用service层的update函数,将用户信息和更新条件封装到updateWrapper中,然后更新用户信息到数据库中。 2.将userInfoVO.getUserId()赋值给holiday对象的userId属性。3.调用iHolidayService.save(holiday)函数,将holiday对象保存到数据库中。4.返回R.ok()表示操作成功。

sysUserService.update(user, updateWrapper);holiday.setUserId(userInfoVO.getUserId());iHolidayService.save(holiday);return R.ok();

完整代码

controller层

public R save(@RequestBody Holiday holiday) {getUser();getUserId();//可以拿主表登录人员的数据UserInfoVO userInfoVO = sysUserService.getLoginUserInfo();//1.UpdateWrapper<SysUserEntity> updateWrapper = new UpdateWrapper<>();//2.SysUserEntity user = sysUserService.getOne(new QueryWrapper<SysUserEntity>().eq("user_id", userInfoVO.getUserId()).select("user_id", "freeze_until").last("limit 1"));//3.long nowTimestamp = new Date().getTime();if (user.getFreezeUntil() == null) {long freezeTimestamp = nowTimestamp + 5 * 60 * 1000;Date freezeUntilDate = new Date(freezeTimestamp);//user.setFreezeUntil(freezeUntilDate);updateWrapper.eq("user_id", userInfoVO.getUserId()).set("freeze_until", freezeUntilDate);} else {// 获取用户现有的冻结结束时间戳long freezeUntilTimestamp = user.getFreezeUntil().getTime();if (nowTimestamp < freezeUntilTimestamp) {throw new IllegalStateException("当前时间小于冻结结束时间,无法操作");} else {// 当前时间已经超过冻结期,则重置冻结时间为当前时间+5分钟long newFreezeTimestamp = nowTimestamp + 5 * 60 * 1000;Date newFreezeUntilDate = new Date(newFreezeTimestamp);// user.setFreezeUntil(newFreezeUntilDate);updateWrapper.eq("user_id", userInfoVO.getUserId()).set("freeze_until", newFreezeUntilDate);}}//holiday.setUserId(getUserId());//更新用户冻结时间//sysUserService.update(user);sysUserService.update(user, updateWrapper);holiday.setUserId(userInfoVO.getUserId());iHolidayService.save(holiday);return R.ok();}

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

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

相关文章

layui中,父页面与子页面,函数方法的相互调用、传参

<%--父页面--%> <script type"text/javascript">var KaoHaoType 0; // 考号类型 自定义参数1// 选取考号类型function SelectKaoHaoType(callBack) {KaoHaoType 0; // 默认选择填涂考号layer.open({type: 2, title: 请选择 考号区类型, ar…

职场中被小人欺负了,应该一笑了之吗?还是怎么办?

在职场中遇到不公正的待遇或被欺负&#xff0c;确实是一个让人困扰的问题。处理这类问题&#xff0c;首先要保持冷静和理性&#xff0c;避免情绪化的反应&#xff0c;这样有助于找到最合适的解决方案。以下是一些建议&#xff0c;您可以根据具体情况考虑&#xff1a; 1. **保持…

网络编程作业day2

1.将TPC和UDP通信模型各敲两遍 &#xff08;1&#xff09;TPC通信模型&#xff1a; 服务器代码&#xff1a; #include <myhead.h> #define SERVER_IP "192.168.125.136" #define SERVER_PORT 1314 int main(int argc, const char *argv[]) {//1、创建用于监…

CLion 2023:专注于C和C++编程的智能IDE mac/win版

JetBrains CLion 2023是一款专为C和C开发者设计的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它集成了许多先进的功能&#xff0c;旨在提高开发效率和生产力。 CLion 2023软件获取 CLion 2023的智能代码编辑器提供了丰富的代码补全和提示功能&#xff0c;使您能够更…

统计业务流量的毫秒级峰值 - 华为机试真题题解

考试平台&#xff1a; 时习知 分值&#xff1a; 200分&#xff08;第二题&#xff09; 考试时间&#xff1a; 两小时&#xff08;共3题&#xff09; 题目描述 业务模块往外发送报文时&#xff0c;有时会出现网卡队列满而丢包问题&#xff0c;但从常规的秒级流量统计结果看&…

Mybatis-Plus介绍

目录 一、Mybatis-Plus简介 1.1、介绍 1.2、特性 1.3、架构 1.4、Mybatis-Plus与Mybatis的区别 二、快速入门 2.1、首先创建数据库mybatis-plus 2.2、创建user表 2.3、插入数据 2.4、创建Spring-Boot项目 2.5、添加依赖 2.6、连接数据库 一、Mybatis-Plus简介 1.1、…

代码随想录第46天|139.单词拆分 多重背包理论基础 背包总结

文章目录 单词拆分思路&#xff1a;代码 多重背包≈0-1背包题目代码 背包总结 单词拆分 3 思路&#xff1a; 代码 class Solution {public boolean wordBreak(String s, List<String> wordDict) {HashSet<String> set new HashSet<>(wordDict);boolean[]…

sheng的学习笔记-卷积神经网络经典架构-LeNet-5、AlexNet、VGGNet-16

目录&#xff1a;目录 看本文章之前&#xff0c;需要学习卷积神经网络基础&#xff0c;可参考 sheng的学习笔记-卷积神经网络-CSDN博客 目录 LeNet-5 架构图 层级解析 1、输入层&#xff08;Input layer&#xff09; 2、卷积层C1&#xff08;Convolutional layer C1&…

Dockerfile(5) - CMD 指令详解

CMD 指定容器默认执行的命令 # exec 形式&#xff0c;推荐 CMD ["executable","param1","param2"] CMD ["可执行命令", "参数1", "参数2"...]# 作为ENTRYPOINT的默认参数 CMD ["param1","param…

VUE3自定义文章排行榜的简单界面

文章目录 一、代码展示二、代码解读三、结果展示 一、代码展示 <template><div class"article-ranking"><div class"header"><h2 class"title">{{ title }}</h2></div><div class"ranking-list&qu…

电子技术——PN结电流关系方程

电子技术——PN结电流关系方程 平衡状态下的PN结 平衡状态下的PN结界面总共有两种电流&#xff0c;一种为 扩散电流 另一种为 漂移电流 。两种电流形成的平衡区域称为 耗散区 。 在平衡状态扩散电流等于漂移电流&#xff0c;此时静电流为0&#xff0c;PN结外部没有电流&…

Java SPI:Service Provider Interface

SPI机制简介 SPI&#xff08;Service Provider Interface&#xff09;&#xff0c;是从JDK6开始引入的&#xff0c;一种基于ClassLoader来发现并加载服务的机制。 一个标准的SPI&#xff0c;由3个组件构成&#xff0c;分别是&#xff1a; Service&#xff1a;是一个公开的接口…

Java ElasticSearch面试题

Java ElasticSearch面试题 前言1、ElasticSearch是什么&#xff1f;2. 说说你们公司ES的集群架构&#xff0c;索引数据大小&#xff0c;分片有多少 &#xff1f;3. ES的倒排索引是什么&#xff1f;4. ES是如何实现 master 选举的?5. 描述一下 ES索引文档的过程&#xff1a;6、…

【Emgu CV教程】7.8、图像锐化(增强)之同态滤波

文章目录 一、同态滤波大体原理二、代码三、效果举例 一、同态滤波大体原理 之前介绍的几个锐化、增强方法&#xff0c;包括更早之前介绍的图像模糊方法&#xff0c;都是基于空间域进行处理&#xff0c;也就是直接对目标点周边像素值进行各种数学运算。而这篇文章提到的同态滤…

喜迎乔迁,开启新章 ▏易我科技新办公区乔迁庆典隆重举行

2024年1月18日&#xff0c;易我科技新办公区乔迁庆典在热烈而喜庆的氛围中隆重举行。新办公区的投入使用&#xff0c;标志着易我科技将以崭新姿态迈向新的发展阶段。 ▲ 易我科技新办公区 随着公司业务的不断发展和壮大&#xff0c;为了更好地适应公司发展的需要&#xff0c;…

2024-02-29(Flink)

1.Flink原理&#xff08;角色分工&#xff09; 2.Flink执行流程 on yarn版&#xff1a; 3.相关概念 1&#xff09;DataFlow&#xff1a;Flink程序在执行的时候会被映射成一个数据流模型&#xff1b; 2&#xff09;Operator&#xff1a;数据流模型中的每一个操作被称作Operat…

npm使用国内淘宝镜像的方法整理

命令配置安装&#xff1a; 淘宝镜像&#xff1a; npm config set registry https://registry.npm.taobao.org/ 官方镜像&#xff1a; npm config set registry https://registry.npmjs.org 通过cnpm安装&#xff1a; npm install -g cnpm --registryhttps://registry.npm.…

PTA L2-003 月饼 (附坑点说明)

月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出的情形是这样的&#…

【Python】PyGameUI控件

哈里前段时间写了一个windows平板上自娱自乐&#xff08;春节和家人一起玩&#xff09;基于pygame的大富翁游戏。 pygame没有按钮之类的UI控件&#xff0c;写起来不怎么顺手。就自己写一个简单的框架。 仓库地址 哈里PygameUi: pygame ui封装自用 (gitee.com) 使用示例 示…

上海亚商投顾:沪指终结月线6连阴 北向资金净买入超160亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日低开高走&#xff0c;沪指重新站上3000点&#xff0c;深成指、创业板指大涨超3%。半导体产业链全…