黑馬點評--短信登錄實現

短信登錄

導入黑馬點評項目

導入資料中提供的SQL文件

其中的核心表有:

  • tb_user :用戶表

  • tb_user_info :用戶詳情表

  • tb_shop:用戶信息表

  • tb_shop_type:商戶類型表

  • tb_blog:用戶日記表(達人探店日記)

  • tb_follow:用戶關注表

  • tb_voucher: 優惠劵表

  • tb_voucher:優惠劵的訂單表

  • tb_voucher_order

注意事項:MySQL版本最好采用5.7及以上版本

該項目為單體項目,采用前后端分離的模式,將后端部署在Tomcat上,前端會將其放置在Nginx服務器上。

在PC端或者移動端在請求頁面時,其實是想Nginx發起請求得到的靜態資源,頁面在通過Ajax像我們的服務端發起請求去查詢數據,這些數據可能來自于Redis集群,或者來自MySQL集群。然后再將查詢到的數據返回給前端,前端完成渲染即可。是一種前后端分離的架構。

還會考慮項目的并發能力,項目還具備水平拓展的能力,即項目部署在Tomcat上后,如果單臺Tomcat上支撐不住,還可以做一個水平的擴展形成一個負載均衡的集群,在多臺Tomcat上部署代碼,一旦步入集群之后,將來就會存在一些集群間的數據共享的問題。

!

導入后端模板

從資料中拿到項目源碼,將其復制到IDEA中,然后利用IDEA打開,注意springboot與MyBatis-plus的版本沖突。

啟動項目后,在瀏覽器訪問:http://localhost:8081/shop-type/list,如果看到數據則證明運行沒有問題

注意事項:不要忘記修改application.yml的MySQL,redis地址信息。

導入前端項目:

該項目將整個前端代碼部署到Nginx里,

在資料中提供了Nginx文件夾,將其復制到任意目錄,確保該目錄不包含中文、特殊字符和空格

在該文件夾下打開命令行界面,輸入 start nginx.exe

然后在瀏覽器中點擊F12,打開手機模式,并訪問 http://localhost:8080

基于session實現登錄

短信登錄包括短信的發送,然后是基于短信驗證碼的登錄,最后是對登錄狀態的一個校驗

發送短信驗證碼

用戶會提交自己的手機號碼,服務端接收到手機號后,我們要去驗證用戶的手機號是否合法。

校驗失敗則重新提交,校驗成功則生成驗證碼,還要去保存驗證碼(我們發送驗證碼的目的是讓用戶去做登錄,需要校驗驗證碼,需要現將其保存到session中)。

最后發送驗證碼。

流程圖如下

實現步驟:

首先查看請求,請求方式為POST,請求路徑為/user/code,請求參數為phone,電話號碼,無返回值

先寫發送手機驗證碼的代碼,業務代碼需要在服務層編譯,所以先在Controller層寫出方法名以及要調用的參數,phone,以及session(將驗證碼保存在session中)

代碼展示:

?
public 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);// 6.返回結果return Result.ok();}

效果展示:

驗證碼:

image-20250523175458482

短信驗證碼登錄、注冊

用戶收到驗證碼則用來做驗證或者登錄。

首先需要提交手機號和驗證碼到后臺,后臺則去驗證提交的兩個數據,先需要校驗驗證碼(將驗證碼與上一步保存在session中的驗證碼做一個比較)。

不一致則會回退上一步,重新提交。如果一樣則根據手機號到數據庫中去查詢該用戶是否存在。

如果不存在,則說明從未訪問過服務器,手機號是全新的,這是需要去將其注冊成一個新用戶,填充一些基本信息,將新用戶寫入到數據庫中。

如果存在,則可以登錄,服務器需要去保存用戶信息到session中。

登錄與注冊是在一個工程中完成的,

流程圖如下

實現步驟:先輸入驗證碼,發送請求并查看,發現為post請求,且負載信息為json字符串,并且傳入的是驗證碼以及電話號碼

image-20250523175655376

思路及代碼展示:

?@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.校驗手機號String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {//2.如果不符合,返回錯誤信息return Result.fail("手機號格式錯誤");}//2.校驗驗證碼Object cacheCode = session.getAttribute("code");String code = loginForm.getCode();// 一般校驗時,從反向校驗,這種校驗不需要if嵌套,否則會嵌套if,避免if嵌套過深if (cacheCode == null || !cacheCode.toString().equals(code)){//3.不一致,返回錯誤信息return Result.fail("驗證碼錯誤");}//4.一致,根據手機號查詢用戶 select ? * from user where phone = ?User user = query().eq("phone", phone).one();//5.判斷用戶是否存在if (user == null){//6.不存在,創建新用戶并保存// 方法定義在函數中 創建用戶//在創建完用戶到數據庫后還需要保存在session中,所以直接賦值給useruser = createUserWithPhone(phone);}//7.保存用戶信息到session并返回結果session.setAttribute("user",user);//這里不需要返回一個登錄憑證,因為用戶信息已經保存在session中,所以不需要返回一個登錄憑證//session的原理是在服務器端保存用戶信息,并在客戶端保存一個標識符。// 這個標識符就是sessionId,這個標識符在客戶端的cookie中保存,這樣客戶端就可以通過這個標識符來獲取服務器端保存的用戶信息。return Result.ok();}

效果展示:

image-20250523185233667

image-20250523185242262

查詢數據庫得用戶插入成功

image-20250523185308689

完成。

校驗登錄狀態

用戶登錄成功后,訪問一些關鍵的數據時需要去校驗登錄狀態。

服務器將用戶信息保存在session中,那將來用戶訪問時,需要基于session進行校驗。

session是基于cookie的,每一個session都會有一個對應的sessionID保存在瀏覽器的cookie中,所以當用戶訪問服務時一定會攜帶cookie(包含sessionID)。

這時服務器就可以基于cookie中的sessionID拿到session,在從session中拿到用戶,服務器只需要驗證session中是否有用戶信息即可。

如果經過判斷沒有用戶,直接攔截請求。

如果判斷存在,就將用戶信息緩存起來到ThreadLocal

(線程域對象,核心作用是讓每一個線程都有自己的數據副本,避免共享資源引發的競態條件。 在實際開發業務中,每一個請求到達微服務時,都是一個獨立的線程,如果沒有使用ThreadLocal,而是將用戶信息存放在本地變量中,可能出現多線程并發修改的安全問題,ThreadLocal會將數據保存到每一個線程的內部,在縣城內部創建一個ThreadLocalMap去保存,每一個線程都有自己獨立的存儲空間,相互之間沒有干擾)

中(在之后的業務中一定會用到當前登錄的用戶信息,將其緩存方便后續的業務使用),然后服務器放行。

流程圖如下:

實現步驟:

在前面已經完成了發送短信驗證碼以及短信驗證碼登錄注冊的功能,但登錄校驗的功能并沒有完成。

登錄校驗的URL為http://localhost:8080/user/me,用來查詢當前登錄的用戶信息,然后服務端返回正確的用戶信息,則登錄校驗成功,并將其跳轉到首頁。

再流程圖中用戶的請求會帶上cookie,而cookie中含有登錄憑證SessionID,而服務端只需要根據sessionID得到session,在從session中獲取用戶,判斷用戶是否存在。

但是其中有些問題:

我們的登錄校驗是在UserController中編譯的,前端會向userController發起請求,而服務端在UserController的是在對應業務中編譯業務邏輯代碼,而在后期,越來越多的業務需要去校驗用戶的登錄,我們不能在每一個Controller中都去編譯一段登錄校驗的業務代碼,

而在springmvc中的攔截器是在所有的controller執行之前去執行,有了攔截器之后,用戶的請求就不能直接去訪問服務端的controller,所有請求必須要先經過攔截器,再由攔截器判斷該不該放行,那就可以將登錄校驗的業務代碼中放到攔截器中去執行。

而攔截器確實可以實現對用戶登錄的校驗,校驗之后,業務可能需要該用戶信息。

但用戶信息卻在攔截器中,Controller并不能拿到。因此需要將攔截器中的用戶信息傳遞到controller中。

需要考慮線程安全問題,把用戶信息存儲在session中,則需要在跨各種處理組件或頁面時都要從session中去取得用戶信息,這增加了訪問session的開銷。

所以我們這里用ThreadLocal。

ThreadLocal就可以解決該問題,ThreadLocal是一個線程域對象,每一個進入Tomcat的請求都是一個獨立的線程,而ThreadLocal的核心作用就是讓每一個線程都有自己的數據副本,避免共享資源引發的競態條件,這樣就可以讓每一個線程都有對應的獨立內存空間取保存對應用戶,線程之間互不干擾,因此無論幾條請求訪問哪些Controller都可以做到獨立線程,都有自己的獨立用戶信息,當需要用戶信息時,則Controller從ThreadLocal中取出用戶即可。

因此我們要在攔截器中實現校驗登錄,以及將用戶信息存入到ThreadLocal。

隱藏用戶敏感信息

服務端在登錄校驗成功后返回的信息較多,包括用戶密碼,時間,電話等敏感信息,存在泄漏風險,在UserController中,登錄校驗的業務代碼拿到的是用戶的全部信息存入session,session是Tomcat的內存空間,這里面的信息越多,對于服務來講,壓力越大。因此不需要存儲全部信息 。而我們在登錄業務中直接將user的全部信息放入session中,因此需要在DTO包下新建UserDTO,里面只有一些不重要的信息屬性字段,因此只需要在登錄業務中在存入session調用hutool中的工具類將User屬性拷貝給UserDTO。

?session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));

然后將攔截器中取出的User對象改為USerDTO對象即可。也需要將UserHolder(用于新建ThreadLocal并調用的工具類)中的User對象改為UserDTO

?public class UserHolder {private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();?public static void saveUser(UserDTO user){tl.set(user);}?public static UserDTO getUser(){return tl.get();}?public static void removeUser(){tl.remove();}}

代碼展示:

在utils包下新建Logininterceptor類(攔截器)

?package com.hmdp.utils;?import com.hmdp.dto.UserDTO;import com.hmdp.entity.User;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import jakarta.servlet.http.HttpSession;import org.springframework.web.servlet.HandlerInterceptor;?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.存在,保存用戶信息到ThreadLocal 并放行// 在工具類中定義了一個UserHolder 是一個線程安全的ThreadLocal變量,用于保存當前線程的用戶信息。// 其中有三個方法:saveUser( 保存),getUser(拿到),removeUser(移除)。UserHolder.saveUser((UserDTO) user);return true;}?//  攔截器 后處理@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//  移除用戶 避免用戶泄漏UserHolder.removeUser();}}

在配置包下新建MVCConfig類

代碼如下:

?package com.hmdp.config;?import com.hmdp.utils.LoginInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor())//  除了這些路徑,其他路徑都進行攔截.excludePathPatterns("/user/code","/user/login","/blog/hot","/voucher/**","/shop/**","/shop-type/**","/upload/**","/blog/query/hot","/druid/**");?}}

至此,校驗登錄狀態業務完成。

測試:

image-20250523230143009

登錄校驗成功,并且數據也未泄露。

希望對大家有所幫助!!

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

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

相關文章

AWS EC2實例安全遠程訪問最佳實踐

EC2 遠程連接方案對比 遠程訪問 Amazon EC2 實例主要有以下四種方式&#xff1a; Secure Shell (SSH) 遠程訪問AWS Systems Manager 會話管理器適用于 Linux 實例的 EC2 Serial ConsoleAmazon EC2 Instance Connect SSH 遠程訪問 SSH&#xff08;Secure Shell&#xff09;廣…

Idea如果有參數,怎么debug

如上圖&#xff0c;輸入輸出路徑是需要運行的時候給參數。 那么 FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); 給上面的代碼給參數的步驟為 1.在類名或者方法名上右鍵&#xff0c;選擇More Run/Debug…

Oracle Apps R12——報表入門2:單表——報表開發流程

☆開發思路 開發表報代碼流程中有幾個重要的組件和重要的知識點需要搞懂&#xff0c;才能得心應手。報表通常是通過表格的形式來存在的&#xff0c;我們一般在開發代碼的時候在【輸出】中打印HTML,Css格式的表格&#xff0c;并把查詢到的數據插入其中&#xff0c;即可完成一個報…

Servlet的繼承關系和生命周期

1.繼承關系&#xff1a; javax.servlet.Servlet接口->javax.servlet.GenericServlet抽象類 ->javax.servlet.http.HttpServlet抽象子類 2.相關方法&#xff1a; javax.servlet.Servlet&#xff1a; &#xff08;1&#xff09;void init(config) -初始化方法 &…

PEFT庫PromptTuningConfig 配置

PEFT庫 PromptTuningConfig 配置 "Prompt Tuning"的參數高效微調 PromptTuningConfig 核心參數解析 1. task_type="CAUSAL_LM" 作用:指定任務類型為因果語言模型(Causal LM)。說明:因果語言模型從左到右生成文本(如GPT系列),這與任務需求匹配(模…

【438. 找到字符串中所有字母異位詞】

Leetcode算法練習 筆記記錄 438. 找到字符串中所有字母異位詞 438. 找到字符串中所有字母異位詞 思路就是我們要找和p相同的詞&#xff0c;可以先排個序&#xff0c;每次取一個和p的size長度相同的窗口去滑動&#xff0c;符合就記錄&#xff0c;不符合繼續滑動。 public List&l…

React Hooks底層執行邏輯詳解、自定義Hooks、FiberScheduler

React Hooks底層執行邏輯詳解 React Hooks 在表面上看像普通的函數調用&#xff0c;背后卻隱藏著一套復雜而高效的運行時機制。要理解 React Hooks 的底層執行邏輯&#xff0c;需要從 React 如何管理組件的狀態與副作用入手。 &#x1f9e0; 一、React 為什么引入 Hooks&#…

Windows命令實用工具——tcping 命令工具安裝及基礎使用

Windows命令實用工具——tcping 命令工具安裝及使用 一、tcping 命令簡介二、tcping 的安裝1、tcping 官網下載安裝包2、將軟件包復制到 Windws 系統的 System32 目錄下面3、查看 tcping 命令是否安裝成功 三、tcping 工具簡單使用方法 一、tcping 命令簡介 tcping 的主要功能…

智慧化工園區安全風險管控平臺建設方案(Word)

1 項目概況 1.1 園區概況 1.1.1 XX化工園區簡況 1.1.2 企業現狀 1.1.3 園區發展方向 1.1.4 園區信息化現狀 1.2 項目建設背景 1.2.1 政策背景 1.3 項目建設需求分析 1.3.1 政策需求分析 1.3.2 安全生產監管需求分析 1.3.3 應急協同管理需求分析 1.3.4 工業互聯網安…

【動手學深度學習】2.3. 線性代數

目錄 2.3. 線性代數1&#xff09;標量2&#xff09;向量3&#xff09;矩陣4&#xff09;張量5&#xff09;張量的基本性質6&#xff09;降維7&#xff09;點積8&#xff09;矩陣-向量積9&#xff09;矩陣-矩陣乘法10&#xff09;范數11&#xff09; 小結 2.3. 線性代數 本節將…

如何在項目當中使用redis進行范圍搜索

目錄 如何將地理位置數據保存到 Redis 中以支持范圍查詢 Redis 中的 GEO 類型是什么&#xff1f; 如何保存 GEO 數據到 Redis 分段解釋&#xff1a; RedisKey.POSTS_ANIMALS_LOCATIONS new Point(longitude, latitude) 如何進行范圍搜索 Redis GEO 范圍搜索核心語句 1…

物聯網低功耗保活協同優化方案:軟硬件與WiFi網關動態聯動

目錄 一、總體方案概述 二、架構組成 2.1 系統拓撲 2.2 硬件端(MCU + WiFi 模組) 2.3 WiFi 網關 2.4 云端服務器 三、低功耗保活技術設計模式 3.1 模式一:定時喚醒 + MQTT 保活 3.1.1 設備端 3.1.2 優勢 3.2 模式二:網關保活代理 + 本地網絡喚醒 3.2.1 網關功能…

UniApp+Vue3微信小程序二維碼生成、轉圖片、截圖保存整頁

二維碼生成工具使用uqrcode/js&#xff0c;版本4.0.7 官網地址&#xff1a;uQRCode 中文文檔&#xff08;不建議看可能會被誤導&#xff09; 本項目采用了npm引入方式&#xff0c;也可通過插件市場引入&#xff0c;使用上會略有不同 準備工作&#xff1a; 安裝&#xff1a;pnpm…

Zenmap代理情況下無法掃描ip

原因是開了代理會報錯 error “only ethernet devices can be used for raw scans on Windows” 在掃描參數后加 -sT -Pn&#xff0c;但會導致結果太多 例如&#xff1a;nmap -sT -T4 -A -v -Pn 10.44.2.0/24 如果你只是想找沒人用的IP&#xff0c;你不需要搞復雜的原始層掃描&…

將多個值關聯到同一個 key的map(key可以重復的map)示例

在 Java 中&#xff0c;標準的 Map 接口要求 key 必須唯一&#xff0c;如果需要 key 可重復 且保持 插入順序 的數據結構&#xff0c;可以使用以下方案&#xff1a; 1. 使用 List<Map.Entry<K, V>> 最直接的方式是用鏈表存儲鍵值對&#xff0c;允許重復 key&…

Arthas(阿爾薩斯)

一、Arthas 是什么&#xff1f; Arthas&#xff08;阿爾薩斯&#xff09;是阿里巴巴開源的一款 Java 在線診斷工具&#xff0c;基于 Java Agent 和字節碼增強技術實現。它無需重啟 JVM&#xff0c;即可動態追蹤代碼執行、實時查看 JVM 狀態、修改代碼邏輯&#xff0c;是生產環…

深入解讀Qwen3技術報告(三):深入剖析Qwen3模型架構

重磅推薦專欄&#xff1a; 《大模型AIGC》 《課程大綱》 《知識星球》 本專欄致力于探索和討論當今最前沿的技術趨勢和應用領域&#xff0c;包括但不限于ChatGPT和Stable Diffusion等。我們將深入研究大型模型的開發和應用&#xff0c;以及與之相關的人工智能生成內容&#xff…

UE4游戲查找本地角色數據的方法-SDK

UE4中&#xff0c;玩家的表示通常涉及以下幾個類&#xff1a; APlayerController: 代表玩家的控制邏輯&#xff0c;處理輸入等。 APawn: 代表玩家在世界中的實體&#xff08;比如一個角色、一輛車&#xff09;。APlayerController 控制一個 APawn。 ACharacter: APawn 的一個…

springboot+vue實現服裝商城系統(帶用戶協同過濾個性化推薦算法)

今天教大家如何設計一個服裝商城 , 基于目前主流的技術&#xff1a;前端vue3&#xff0c;后端springboot。 同時還帶來的項目的部署教程。 系統最大的亮點是使用了兩個推薦算法: 1. 基于Jaccard算法的用戶瀏覽歷史推薦。 2. 基于用戶的協同過濾算法個性化推薦。 還有核心的商…

ERROR: Could not install packages due to an OSError: [WinError 5] 拒絕訪問

有可能是設置了代理 unset ALLPROXY 或者注釋掉 當然也有可能是其他原因 權限不足?? 以管理員身份運行 CMD/PowerShell&#xff0c;或使用 --user 安裝 ??文件被占用?? 關閉殺毒軟件或重啟電腦 Python 環境損壞?? 重新安裝 Python 或使用虛擬環境 ?? 殺毒軟件阻止…