【從零開始學習Redis】項目實戰-黑馬點評D1

項目實戰-黑馬點評

項目架構

短信登錄

發送短信驗證碼

實現思路就是按照上圖左一部分,

實現類如下

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** 驗證手機號發送驗證碼** @param phone* @param session* @return*/@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {//2.如果不符合,可以返回錯誤信息return Result.fail("手機號格式錯誤");}//3.生成驗證碼String code = RandomUtil.randomNumbers(6);//4.保存驗證碼到sessionsession.setAttribute("code", code);//5.發送驗證碼log.debug("發送驗證碼成功,驗證碼為:{}",code);//返回okreturn Result.ok();}
}

這里封裝了RegexUtils工具類,調用了反向驗證手機號方法,我們可以學習一下這個工具類的實現

public class RegexUtils {/*** 是否是無效手機格式* @param phone 要校驗的手機號* @return true:符合,false:不符合*/public static boolean isPhoneInvalid(String phone){return mismatch(phone, RegexPatterns.PHONE_REGEX);}/*** 是否是無效郵箱格式* @param email 要校驗的郵箱* @return true:符合,false:不符合*/public static boolean isEmailInvalid(String email){return mismatch(email, RegexPatterns.EMAIL_REGEX);}/*** 是否是無效驗證碼格式* @param code 要校驗的驗證碼* @return true:符合,false:不符合*/public static boolean isCodeInvalid(String code){return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);}// 校驗是否不符合正則格式private static boolean mismatch(String str, String regex){if (StrUtil.isBlank(str)) {return true;}return !str.matches(regex);}
}

其實就是先檢查手機號是否為空,如果不為空再把當前手機號字符串按照正則表達式匹配。

短信驗證碼登錄

校驗手機號,校驗驗證碼,如果不一致直接返回錯誤信息。

如果一致,需要查詢用戶,如果根據當前手機號,用戶不存在,那么創建新用戶,其實就是insert

這里沒有MapperMyBatis-PlusMyBatis的增強工具,內置了大量的方法,無需XML就能完成CRUD

/*** 實現登錄功能* @param loginForm* @param session* @return*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {String phone = loginForm.getPhone();//1.校驗手機號if(RegexUtils.isPhoneInvalid(phone)){//手機號不符合,返回錯誤信息return Result.fail("手機號格式錯誤");}//2.校驗驗證碼Object cacheCode = session.getAttribute("code");String code = loginForm.getCode();if(cacheCode == null || !cacheCode.equals(code)){//3.不一致,報錯return Result.fail("驗證碼錯誤");}//4.一致,根據手機號查詢用戶 select * from tb_user where phone = ?User user = query().eq("phone", phone).one();//5.判斷用戶是否存在if(user == null){//6.不存在,創建新用戶并保存user = createUserWithPhone(phone);}//7.保存用戶信息到sessionsession.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));return Result.ok();
}private User createUserWithPhone(String phone){
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
save(user);
return user;
}
登錄校驗

在訪問后端的多個接口的時候,不可能每次訪問都得登陸,保證只要登陸一次即可。于是可以使用攔截器統一攔截,獲取session信息,如果存在那么放行,并把信息保存到ThreadLocal,保證可以隨時調用。如果不存在,那么攔截。

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.獲取sessionHttpSession session = request.getSession();//2.獲取session的用戶Object user = session.getAttribute("user");//3.判斷用戶是否存在if(user == null){//4.不存在,攔截,返回401狀態碼response.setStatus(401);return false;}//5.存在,保存用戶信息到ThreadLocalUserHolder.saveUser((UserDTO) user);//6.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}
集群的session共享問題

由于多臺Tomcat并不共享session的共享空間,請求切換到不同的Tomcat時就會導致數據丟失。

最開始考慮的是,只要把數據拷貝一份到每個Tomcat即可,但會導致空間浪費問題,因為保存的都是相同數據。

替代方案應滿足:

  • 數據共享
  • 內存存儲
  • key、value結構

顯然可以借助Redis

基于Redis實現共享session登錄

發送驗證碼

改動的地方就是本來儲存在session中,現在把驗證碼保存到Redis中,使用的key是業務+手機號,從而保證唯一性。

@Resource
private StringRedisTemplate stringRedisTemplate;
/*** 驗證手機號發送驗證碼** @param phone* @param session* @return*/
@Override
public Result sendCode(String phone, HttpSession session) {//1.校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {//2.如果不符合,可以返回錯誤信息return Result.fail("手機號格式錯誤");}//3.生成驗證碼String code = RandomUtil.randomNumbers(6);//4.保存驗證碼到session set key value ex 120stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone, code,RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);//5.發送驗證碼log.debug("發送驗證碼成功,驗證碼為:{}",code);//返回okreturn Result.ok();
}
實現登錄功能

更新部分在于,取數據從redis中獲取,生成的隨機token作為令牌和儲存用戶信息的key。

/*** 實現登錄功能* @param loginForm* @param session* @return*/
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {String phone = loginForm.getPhone();//1.校驗手機號if (RegexUtils.isPhoneInvalid(phone)) {//手機號不符合,返回錯誤信息return Result.fail("手機號格式錯誤");}//2.校驗驗證碼String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);String code = loginForm.getCode();if (cacheCode == null || !cacheCode.equals(code)) {//3.不一致,報錯return Result.fail("驗證碼錯誤");}//4.一致,根據手機號查詢用戶 select * from tb_user where phone = ?User user = query().eq("phone", phone).one();//5.判斷用戶是否存在if (user == null) {//6.不存在,創建新用戶并保存user = createUserWithPhone(phone);}//7.保存用戶信息到redis//7.1 隨機生成token,作為登錄令牌String token = UUID.randomUUID().toString(true);//7.2 將user對象轉為HashMap存儲UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().ignoreNullValue().setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));//7.3 儲存String userKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(userKey, userMap);//7.4 設置token有效期stringRedisTemplate.expire(userKey, LOGIN_USER_TTL, TimeUnit.MINUTES);//8.返回tokenreturn Result.ok(token);
}private User createUserWithPhone(String phone) {
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
save(user);
return user;
}
登錄攔截器的優化

我們當前的攔截器存在一個問題,就是攔截了需要登錄的請求時,為了避免永久數據對redis的壓力,我們在執行登陸后,一般會給token設置有效期,意思是長時間不點擊訪問頁面,那么token就會過期,不再允許訪問。但是現在只有一個攔截器,攔截的是需要登錄的路徑,而且只有在用戶登錄時會更新token有效期,這樣就會導致即使用戶在不停操作,但是不需要登錄操作的部分功能也不會更新token有效期,同時需要登陸操作的部分也不會更新token有效期。

怎么解決呢?

我們可以定義兩個攔截器,一個用來攔截所有路徑,但是不做“攔截”處理,主要負責獲取token、查詢Redis用戶、保存到ThreadLocal、更新token有效期、放行。另外一個攔截需要登陸的路徑,查詢ThreadLocal用戶,不存在就攔截,存在則放行。

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

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

相關文章

自然語言處理的范式轉變:從Seq2Seq模型到Transformer架構

Seq2Seq 定義 Seq2Seq是一個Encoder-Decoder結構的網絡&#xff0c;它的輸入是一個序列&#xff0c;輸出也是一個序列&#xff0c; Encoder使用循環神經網絡(RNN,GRU&#xff0c;LSTM等)&#xff0c;將一個可變長度的信號序列(輸入句子)變為固定維度的向量編碼表達&#xff0c;…

【博客系統測試報告】---接口自動化測試

目錄 1、需求分析 2、挑選接口 3、設計博客系統的測試用例 4、設計自動化測試框架 test_add.py: test_detail.py: test_getAuthorInfo.py: test_getUserInfo: test_list.py: test_login.py: logger_util.py: request_util.py: yaml_util.py: 1、需求分析 根據業務…

Mysql數據庫遷移到GaussDB注意事項

mysql數據庫遷移高斯數據庫 建議開啟高斯數據庫M模式&#xff0c;mysql兼容模式&#xff0c;可以直接使用mysql的建表語句&#xff0c;自增主鍵可以使用AUTO_INCREMENT&#xff0c;如果不開啟M模式&#xff0c;只能使用高斯數據庫的序列添加自增主鍵1&#xff1a;如果使用數據庫…

蘋果正計劃大舉進軍人工智能硬件領域

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

Serverless 架構核心解析與應用實踐

Serverless 的核心定義與優勢??核心定義Serverless&#xff08;無服務器架構&#xff09;是一種云計算模型&#xff0c;開發者無需關注底層服務器管理&#xff0c;由云服務商自動分配資源、彈性擴縮容&#xff0c;并按實際使用量計費?。其核心特點包括&#xff1a;?按需計算…

Redis持久化機制詳解:RDB與AOF的全面對比與實踐指南

目錄 一、RDB持久化機制 1.1 RDB概述 1.2 RDB觸發機制 1) 手動執行save命令 2) 手動執行bgsave命令 3) Redis正常關閉時 4) 自動觸發條件滿足時 1.3 RDB詳細配置 1.4 RDB實現原理 1.5 RDB的優缺點分析 二、AOF持久化機制 2.1 AOF概述 2.2 AOF工作流程 2.3 AOF同步…

介紹一下jQuery的AJAX異步請求

目錄 一、核心方法&#xff1a;$.ajax() 二、簡化方法&#xff08;常用場景&#xff09; 1. $.get()&#xff1a;快速發送 GET 請求&#xff08;獲取數據&#xff09; 2. $.post()&#xff1a;快速發送 POST 請求&#xff08;提交數據&#xff09; 3. $.getJSON()&#xf…

Win10系統Ruby+Devkit3.4.5-1安裝

Win10系統RubyDevkit3.4.5-1安裝安裝步驟軟件工具安裝Ruby安裝gem mysql2處理libmysql.dll驗證mysql2安裝步驟 軟件工具 mysql-connector-c-6.1.11-winx64.zip rubyinstaller-devkit-3.4.5-1-x64.exe 安裝Ruby 執行rubyinstaller-devkit-3.4.5-1-x64.exe&#xff0c;期間可…

社交工程:洞穿人心防線的無形之矛

在網絡安全領域&#xff0c;一道無形的裂痕正在迅速蔓延。它不是復雜的零日漏洞&#xff0c;也不是精妙的惡意代碼&#xff0c;而是利用人性弱點進行攻擊的古老技藝——社交工程。當全球網絡安全支出突破千億美元大關&#xff0c;防火墻筑得越來越高&#xff0c;加密算法越來越…

Go 并發控制利器 ants 使用文檔

https://github.com/panjf2000/ants1.1 什么是 ants ants 是一個高性能的 Go 語言 goroutine 池&#xff0c;它能復用已完成任務的 goroutine&#xff0c;避免頻繁創建和銷毀 goroutine&#xff0c;節省 CPU 與內存開銷&#xff0c;并且能限制并發數量防止資源被耗盡。 1.2 安裝…

Day57--圖論--53. 尋寶(卡碼網)

Day57–圖論–53. 尋寶&#xff08;卡碼網&#xff09; 今天學習&#xff1a;最小生成樹。有兩種算法&#xff08;Prim和Kruskal&#xff09;和一道例題。 prim 算法是維護節點的集合&#xff0c;而 Kruskal 是維護邊的集合。 最小生成樹&#xff1a;所有節點的最小連通子圖&am…

解決海洋探測數據同步網絡問題的新思路——基于智能組網技術的探索

隨著海洋探測技術的不斷發展&#xff0c;數據同步網絡的穩定性和低延遲需求變得愈發重要。海洋探測數據來自多個分布式采集點&#xff0c;這些點需要高效的組網方式來實現實時數據傳輸。然而&#xff0c;由于海洋環境的特殊性&#xff08;如復雜的網絡拓撲、高濕度和極端溫度&a…

設計模式筆記_行為型_責任鏈模式

1. 責任鏈模式介紹責任鏈模式&#xff08;Chain of Responsibility&#xff09;是一種行為設計模式&#xff0c;它允許將多個處理器&#xff08;處理對象&#xff09;連接成一條鏈&#xff0c;并沿著這條鏈傳遞請求&#xff0c;直到有一個處理器處理它為止。職責鏈模式的主要目…

pygame的幀處理中,涉及鍵盤的有`pg.event.get()`與`pg.key.get_pressed()` ,二者有什么區別與聯系?

一、pg.event.get() 返回的是一組事件 pg.event.get() 返回的是一組事件&#xff08;一個包含多個事件對象的列表&#xff09;。這是因為在游戲的“一幀”時間內&#xff08;通常1/60秒左右&#xff09;&#xff0c;用戶可能會觸發多個事件&#xff08;比如同時按下多個鍵、快速…

TF - IDF算法面試與工作常見問題全解析

在自然語言處理領域&#xff0c;TF - IDF算法是一個基礎且重要的概念。無論是在求職面試還是在實際工作中&#xff0c;都經常會遇到與TF - IDF相關的問題。以下是一些常見的問題及其詳細解答&#xff1a; 一、基本概念類問題 1. 什么是TF - IDF算法&#xff1f; TF - IDF&#…

Transformer網絡結構解析

博主會經常分享自己在人工智能階段的學習筆記&#xff0c;歡迎大家訪問我滴個人博客&#xff01;&#xff08;都不白來&#xff01;&#xff09; 小牛壯士 - 個人博客https://kukudelin.top/ 前言 Transformer 廣泛應用于自然語言處理&#xff08;如機器翻譯、文本生成&…

gateway進行接口日志打印

打印需求&#xff1a;對所有的接口打印&#xff1a;請求方式&#xff0c;請求路徑&#xff0c;請求參數&#xff0c;用戶id&#xff0c;訪問IP&#xff0c;訪問時間對增刪改操作的接口打印&#xff1a;接口響應打印方案&#xff1a;給GET設置一個白名單&#xff08;因為get請求…

MATLAB實現圖像增強(直方圖均衡化)

直方圖均衡化是一種常用的圖像增強技術&#xff0c;它通過重新分布圖像的像素強度值來增強圖像的對比度。以下是MATLAB中實現直方圖均衡化的詳細方法。%% 直方圖均衡變換 clc;close all;clear all;warning off;%清除變量 rand(seed, 100); randn(seed, 100); format long g;%% …

java15學習筆記-密封類

360:Sealed Classes (Preview) 封閉類&#xff08;預覽&#xff09; 總結 使用密封類和接口增強Java編程語言。密封類和接口限制了哪些其他類或接口可以擴展或實現它們。這是JDK 15中的預覽語言功能。 目標 允許類或接口的作者控制負責實現它的代碼。 提供一種比訪問…

西門子PLC通過穩聯技術EtherCAT轉Profinet網關連接baumuller伺服器的配置案例

西門子PLC用穩聯技術的EtherCAT轉Profinet網關&#xff0c;連上baumuller伺服器的配置例子本案例實現西門子S71200 PLC通過EtherCAT轉Profinet網關對baumuller&#xff08;Baumller&#xff09;伺服器的實時控制&#xff0c;適用于高精度運動控制場景&#xff08;如精密機床、自…