黑馬點評-項目集成git及redis實現短信驗證碼登錄

目錄

IDEA集成git

傳統session存在的問題?

redis方案

業務流程

選用的數據結構

整體訪問流程

發送短信驗證碼

?獲取校驗驗證碼

配置登錄攔截器

攔截器注冊配置類

攔截器

用戶狀態刷新問題

刷新問題解決方案


IDEA集成git

遠程倉庫采用碼云,創建好倉庫,復制倉庫的url

?在idea中點擊,出現git選項,點擊ok

?之后右擊項目,點擊remotes

?填寫url即可集成git

傳統session存在的問題?

傳統的登錄認證會采用session進行登錄認證,將登錄的驗證碼,用戶信息都存放到session中,我們通過session來進行操作數據,這有什么問題呢

每個tomcat服務器中都有一份屬于自己的session,假設用戶第一次訪問第一臺tomcat,并且把自己的信息存放到第一臺服務器的session中,但是第二次這個用戶訪問到了第二臺tomcat,那么在第二臺服務器上,肯定沒有第一臺服務器存放的session,即session在各個服務器之間是不共通的,所以此時整個登錄攔截功能就會出現問題,而redis數據本身就是共享的,就可以避免session共享的問題了

redis方案

業務流程

  • 將驗證碼存儲到redis中
  • 將用戶數據存儲到redis中

選用的數據結構

存儲驗證碼時采用String類型,key采用業務代碼+手機號

存儲user數據時采用hash,key采用業務代碼+隨機的tonke

hash可以將對象中的每個字段獨立存儲,可以針對單個字段做CRUD,并且內存占用更少,實際上也可以采用String類型,但hash類型消耗內存較少,故選用String類型

整體訪問流程

?當注冊完成后,用戶去登錄會去校驗用戶提交的手機號和驗證碼,是否一致,如果一致,則根據手機號查詢用戶信息,不存在則新建,最后將用戶數據保存到redis,并且生成token作為redis的key,當我們校驗用戶是否登錄時,會去攜帶著token進行訪問,從redis中取出token對應的value,判斷是否存在這個數據,如果沒有則攔截,如果存在則將其保存到threadLocal中,簡化后續業務獲取用戶信息的代碼,后續業務需要登錄用戶的信息,只要在threadLocal中取即可,無需從redis中取從而增加復雜度,最后放行。

redis業務代碼常量(后續繼續補充)

public class RedisConstants {//發送驗證碼業務標識public static final String LOGIN_CODE_KEY = "login:code:";public static final Long LOGIN_CODE_TTL = 2L;//用戶登錄業務標識public static final String LOGIN_USER_KEY = "login:token:";public static final Long LOGIN_USER_TTL = 30L;}

發送短信驗證碼

采用日志打印形式,將驗證碼打印在控制臺

 public Result sendCode(String phone) {//校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號碼格式不正確");}//手機號格式正確生成驗證碼String code = RandomUtil.randomNumbers(6);//保存到redisstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);//打印日志log.debug("手機驗證碼為:" + code);return Result.ok();}

?獲取校驗驗證碼

我們需要對用戶敏感信息進行篩選,只給前端返回必要的用戶信息數據,需要封裝dto對象,同時在uuid生成token,作為redis取數據的key,最后要設置過期時間,防止reids內存爆炸,設置為30分鐘,因為session的過期時間也是30分鐘

 public Result login(LoginFormDTO loginForm) {String phone = loginForm.getPhone();String code = loginForm.getCode();//校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手機號碼格式不正確");}//從redis取驗證碼,進行比對String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);if (cacheCode == null || !cacheCode.equals(code)) {return Result.fail("驗證碼錯誤");}//根據電話號碼從數據庫查詢用戶是否存在LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getPhone, phone);User user = userMapper.selectOne(queryWrapper);//不存在則創建if (user == null) {user = createUserWithPhone(phone);}String token = UUID.randomUUID().toString(true);//只返回不敏感的信息,使用dto進行封裝UserDTO userDTO = new UserDTO();BeanUtils.copyProperties(user, userDTO);//將dto對象轉化為map結構Map<String, String> userMap = new HashMap<>();userMap.put("id", userDTO.getId().toString());userMap.put("icon", userDTO.getIcon());userMap.put("nickName", userDTO.getNickName());
//業務代碼+token形成redis中的keyString tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);return Result.ok(token);}

配置登錄攔截器

攔截器注冊配置類

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登錄攔截器registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login");}
}

攔截器

攔截器需要的stringRedisTemplate對象不能通過@Autowired直接注入

因為LoginInterceptor 對象是我們手動創建的,不受spring管控,不在spring容器中,故不能注入spring容器中的bean,只能通過在配置類中通過構造方法注入stringRedisTemplate對象

public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.獲取請求頭中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {response.setStatus(401);return false;}String tokenKey = LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);//3.判斷用戶是否存在if (userMap.isEmpty()) {//4.不存在,攔截,返回401狀態碼response.setStatus(401);return false;}UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//5.存在,保存用戶信息到ThreadlocalUserHolder.saveUser(userDTO);//刷新登錄狀態stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);//6.放行return true;}
}

用戶狀態刷新問題

那就是登錄認證和刷新用戶狀態綁定在一個攔截器中,而需要登錄認證的路徑并不是全部路徑,如果用戶不訪問需要登錄認證的路徑,那就刷新不了用戶狀態,即30分鐘之后登錄就會退出,這明顯不符合我們的常識

刷新問題解決方案

我們可以再加一個攔截器,形成一個攔截器鏈,第一個攔截器對所有路徑進行攔截,而它的功能就是刷新登錄狀態,登錄認證則由第二個攔截器完成

?

?刷新狀態攔截器

public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.獲取請求頭中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {return true;}// 2.基于TOKEN獲取redis中的用戶String key  = LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);// 3.判斷用戶是否存在if (userMap.isEmpty()) {return true;}// 5.將查詢到的hash數據轉為UserDTOUserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6.存在,保存用戶信息到 ThreadLocalUserHolder.saveUser(userDTO);// 7.刷新token有效期stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 移除用戶UserHolder.removeUser();}

登錄認證攔截器

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.判斷是否需要攔截(ThreadLocal中是否有用戶)if (UserHolder.getUser() == null) {// 沒有,需要攔截,設置狀態碼response.setStatus(401);// 攔截return false;}// 有用戶,則放行return true;}
}

為了保證攔截器的執行先后順序,配置類需設置order的大小,設定攔截器執行的先后的順序,如若不設置,就按照注冊順序的先后來執行

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登錄攔截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);// token刷新的攔截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42636.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42636.shtml

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

相關文章

【O2O領域】Axure外賣訂餐騎手端APP原型圖,外賣配送原型設計圖

作品概況 頁面數量&#xff1a;共 110 頁 兼容軟件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 應用領域&#xff1a;外賣配送、生鮮配送 作品申明&#xff1a;頁面內容僅用于功能演示&#xff0c;無實際功能 作品特色 本品為外賣訂餐騎手端APP原型設計圖&#x…

CentOS下MySQL的徹底卸載的幾種方法

這里我為大家詳細講解下“CentOS下MySQL的徹底卸載的幾種方法”的完整攻略。 一、關閉MySQL服務 在開始操作之前&#xff0c;需要先關閉MySQL服務。可以使用以下命令來關閉MySQL服務&#xff1a; systemctl stop mysqld 或者 service mysqld stop 二、使用yum命令卸載MySQL…

微前端 - qiankun

qiankun 是一個基于 single-spa 的微前端實現庫&#xff0c;旨在幫助大家能更簡單、無痛的構建一個生產可用微前端架構系統。 本文主要記錄下如何接入 qiankun 微前端。主應用使用 vue2&#xff0c;子應用使用 vue3、react。 一、主應用 主應用不限技術棧&#xff0c;只需要提…

數據結構之線性表的類型運用Linear Lists: 數組,棧,隊列,鏈表

線性表 定義 一個最簡單&#xff0c;最基本的數據結構。一個線性表由多個相同類型的元素穿在一次&#xff0c;并且每一個元素都一個前驅&#xff08;前一個元素&#xff09;和后繼&#xff08;后一個元素&#xff09;。 線性表的類型 常見的類型&#xff1a;數組、棧、隊列…

mysql、redis面試題

mysql 相關 1、數據庫優化查詢方法 外鍵、索引、聯合查詢、選擇特定字段等等2、簡述mysql和redis區別 redis&#xff1a; 內存型非關系數據庫&#xff0c;數據保存在內存中&#xff0c;速度快mysql&#xff1a;關系型數據庫&#xff0c;數據保存在磁盤中&#xff0c;檢索的話&…

[Go版]算法通關村第十二關黃金——字符串沖刺題

目錄 題目&#xff1a;最長公共前綴解法1&#xff1a;縱向對比-循環內套循環寫法復雜度&#xff1a;時間復雜度 O ( n ? m ) O(n*m) O(n?m)、空間復雜度 O ( 1 ) O(1) O(1)Go代碼 解法2&#xff1a;橫向對比-兩兩對比&#xff08;類似合并K個數組、合并K個鏈表&#xff09;復…

okhttp下載文件 Java下載文件 javaokhttp下載文件 下載文件 java下載 okhttp下載 okhttp

okhttp下載文件 Java下載文件 javaokhttp下載文件 下載文件 java下載 okhttp下載 okhttp 1、引入Maven1.1、okhttp發起請求官網Demo 2、下載文件3、擴充&#xff0c;讀寫 txt文件內容3.1讀寫內容 示例 http客戶端 用的是 okhttp&#xff0c;也可以用 UrlConnetcion或者apache …

SD WebUI 擴展:prompt-all-in-one

sd-webui-prompt-all-in-one 是一個基于 Stable Diffusion WebUI 的擴展&#xff0c;旨在提高提示詞/反向提示詞輸入框的使用體驗。它擁有更直觀、強大的輸入界面功能&#xff0c;它提供了自動翻譯、歷史記錄和收藏等功能&#xff0c;它支持多種語言&#xff0c;滿足不同用戶的…

[MAUI]在.NET MAUI中實現可拖拽排序列表

文章目錄 創建可拖放控件創建綁定服務類拖拽&#xff08;Drag&#xff09;拖拽懸停&#xff0c;經過&#xff08;DragOver&#xff09;釋放&#xff08;Drop&#xff09; 創建頁面元素最終效果項目地址 .NET MAUI 中提供了拖放(drag-drop)手勢識別器&#xff0c;允許用戶通過拖…

Mysql驅動包下載

第一步&#xff1a;下載地址 MySQL :: Download Connector/J 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a;解壓 第五步&#xff1a;找到驅動包&#xff0c;放入項目使用即可

管理類聯考——邏輯——真題篇——按知識分類——匯總篇——三、綜合推理

文章目錄 題-綜合推理-分類1-排序真題&#xff08;2016-54-55&#xff09;-難度最高*****-綜合推理-分類1-排序-畫表排除法真題&#xff08;2016-54&#xff09;真題&#xff08;2016-55&#xff09;真題&#xff08;2019-36&#xff09;-綜合推理-分類1-排序真題&#xff08;2…

【AIGC】 國內版聊天GPT

國內版聊天GPT 引言一、國內平臺二、簡單體驗2.1 提問2.2 角色扮演2.3 總結畫圖 引言 ChatGPT是OpenAI發開的聊天程序&#xff0c;功能強大&#xff0c;可快速獲取信息&#xff0c;節省用戶時間和精力&#xff0c;提供個性化的服務。目前國產ChatGPT&#xff0c;比如文心一言&a…

OJ練習第151題——克隆圖

克隆圖 力扣鏈接&#xff1a;133. 克隆圖 題目描述 給你無向 連通 圖中一個節點的引用&#xff0c;請你返回該圖的 深拷貝&#xff08;克隆&#xff09;。 示例 分析 對于一張圖而言&#xff0c;它的深拷貝即構建一張與原圖結構&#xff0c;值均一樣的圖&#xff0c;但是…

C++中的類型擦除技術

文章目錄 一、C類型擦除Type Erasure技術1.虛函數2.模板和函數對象 二、任務隊列1.基于特定類型的方式實現2.基于任意類型的方式實現 參考&#xff1a; 一、C類型擦除Type Erasure技術 C中的類型擦除&#xff08;Type Erasure&#xff09;是一種技術&#xff0c;用于隱藏具體類…

Electron基礎篇

人生有些事,錯過一時,就錯過一世。 官網&#xff1a;簡介 | Electron Electron-大多用來寫桌面端軟件 Electron介紹 Electront的核心組成是Chromium、Node.js以及內置的Native API&#xff0c;其中Chromium為Electron提供強大的UI能力&#xff0c;可以在不考慮兼容的情況下利…

使用神卓互聯內網穿透搭建遠程訪問公司ERP系統

神卓互聯是一款企業級內網穿透軟件&#xff0c;可以將內網中的服務映射到公網上&#xff0c;實現內網服務的訪問。通過神卓互聯&#xff0c;您可以遠程訪問ERP系統。在使用神卓互聯進行內網穿透時&#xff0c;您只需要在生成的公網地址后面加上ERP系統的端口號&#xff0c;即可…

NVIDIA vGPU License許可服務器高可用全套部署秘籍

第1章 前言 近期遇到比較多的場景使用vGPU&#xff0c;比如Citrix 3D場景、Horizon 3D場景&#xff0c;還有AI等&#xff0c;都需要使用顯卡設計研發等&#xff0c;此時許可服務器尤為重要&#xff0c;許可斷掉會出現掉幀等情況&#xff0c;我們此次教大家部署HA許可服務器。 …

【.net】本地調試運行只能用localhost的問題

【.net】本地調試運行只能用localhost的問題 解決方案 找到到項目目錄下 隱藏文件夾 .vs /項目名稱/config/applicationhost.config <bindings><binding protocol"http" bindingInformation"*:1738:localhost" /></bindings> 再加一條你…

職業學院物聯網實訓室建設方案

一、概述 1.1專業背景 物聯網&#xff08;Internet of Things&#xff09;被稱為繼計算機、互聯網之后世界信息產業第三次浪潮&#xff0c;它并非一個全新的技術領域&#xff0c;而是現代信息技術發展到一定階段后出現的一種聚合性應用與技術提升&#xff0c;是隨著傳感網、通…

如何判斷自己是否適合游戲開發?

引言 游戲開發是一個充滿創意和技術挑戰的領域&#xff0c;吸引著越來越多的年輕人投身其中。然而&#xff0c;要想在游戲開發領域獲得成功&#xff0c;首先需要明確自己是否適合這個領域。本文將為你介紹一些判斷自己是否適合游戲開發的關鍵因素。 1. 技術興趣和編程能力 游…