在原有項目進行業務邏輯開發:同一用戶短時間不得提交多次申請,以及更新主表時數據刷新掉了角色權限以及密碼重置的問題,詳細思路及代碼

開發背景:

????????用戶提交表單后,插入到對應數據庫表的字段中去,因需要保存是哪一個用戶提交的,所以需要拿到主表的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.pswp.cn/news/710732.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/710732.shtml
英文地址,請注明出處:http://en.pswp.cn/news/710732.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Spring連載】使用Spring Data訪問 MongoDB----對象映射之對象引用

【Spring連載】使用Spring Data訪問 MongoDB----對象映射之對象引用 一、使用DBRefs 一、使用DBRefs

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. **保持…

如何使用 Socket.IO、Angular 和 Node.js 創建實時應用程序

介紹 WebSocket 是一種允許服務器和客戶端之間進行全雙工通信的互聯網協議。該協議超越了典型的 HTTP 請求和響應范式。通過 WebSocket&#xff0c;服務器可以向客戶端發送數據&#xff0c;而無需客戶端發起請求&#xff0c;因此可以實現一些非常有趣的應用程序。 在本教程中…

網絡編程作業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[]…

15個非常實用的JavaScript技巧,提高你的開發效率

本文我們將探討15個實用的JavaScript技巧&#xff0c;希望它們可以幫你提高開發效率&#xff0c;有用的話點贊收藏~。 1. 反轉字符串 你有時候可能需要將字符串顛倒過來。在JavaScript中&#xff0c;有一個巧妙的一行代碼可以實現這個目標&#xff1a; const reversedString…

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…

根據A(String)字段去重,并且選擇B(Ingter)字段最大的值

數據格式&#xff1a; [SkillDTO(Job電線工, rankGrade高級工, r4), SkillDTO(Job監察員, rankGrade技師, r5), SkillDTO(Job監察員, rankGrade高級工, r4), SkillDTO(skillJob監察員, rankGrade中級工, r3)] List<SkillDTO> resultList SkillDTOList.stream().coll…

電子技術——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、…

Centos系統(Linux)掛載硬盤/數據盤詳細操作和開機自動掛載的兩種方式

前提&#xff1a;已經做好磁盤陣列&#xff0c;將磁盤劃分好 磁盤初始化操作步驟&#xff08;如果已經可以正常掛載可跳過)&#xff1a; 使用fdisk -l命令查看多出來的大容量的磁盤名稱&#xff08;如果多塊磁盤&#xff0c;查看需要掛載的磁盤名稱&#xff09;&#xff0c;一…

embedding的原理和結構

embedding(向量化)是一個將數據轉化為向量矩陣的過程&#xff0c;作用是&#xff1a;將高維稀疏向量轉化為稠密向量&#xff0c;從而方便下游模型處理 簡單的概念大家應該都知道了&#xff0c;以LLM為例 輸入&#xff1a;文字 模型&#xff1a;embedding 輸出&#xff1a;向量…

c++高精度乘法的原理及c++代碼講解

高精度乘法的原理主要是利用數學中乘法的基本原理&#xff0c;將大整數拆分成各個位數的相乘&#xff0c;然后累加得到最終結果。其過程如下&#xff1a; 將兩個大整數相乘&#xff0c;從低位開始逐位相乘&#xff0c;得到部分乘積&#xff1b;將每一位的部分乘積相加&#xf…