【SpringBoot】深入解析使用配置文件解決硬編碼問題綜合練習(三):解析驗證碼拓展問題

校驗輸入驗證碼接口 check( )


5. 為什么要用靜態內部類接收配置文件中的 Seisson 對象?


在這里插入圖片描述

為什么我們接收配置文件的 Session 對象時,使用靜態內部類給 Session 對象的 key,date 屬性賦值呢?不加 static 可以嗎?

CaptchaProperties 類中,Session 被定義為 靜態內部類(static nested class),而不是普通的內部類(non-static inner class)。這兩種方式有一些關鍵區別,會影響配置綁定的行為。


1. 為什么使用 static 內部類?


? 原因 1:Spring Boot 配置綁定要求

  • Spring Boot 的 @ConfigurationProperties 綁定機制要求嵌套類必須是 static,否則無法正確注入屬性。
  • 如果 Session 不是 static,Spring 在解析 YAML/Properties 時無法實例化它,導致 keydate 始終為 null

? 原因 2:避免隱含的外部類依賴

  • 非靜態內部類(non-static inner class)會隱式持有外部類的引用CaptchaProperties.this),這在配置綁定時是不必要的,甚至可能導致問題。
  • 靜態內部類是獨立的,不依賴外部類實例,更適合作為純數據載體(DTO)。

2. 不加 static 可以嗎?


? 不可以(在 @ConfigurationProperties 場景下)

  • 如果去掉 static,Spring 將無法正確綁定嵌套屬性,你會得到類似這樣的錯誤:

    captcha:session:  # 無法注入,因為 Session 不是靜態的key: CAPTCHA_KEYdate: CAPTCHA_DATE
    
  • 即使沒有報錯,session.keysession.date 也會是 null,因為 Spring 無法實例化非靜態內部類(它需要先實例化外部類)。


3. 驗證實驗

你可以嘗試去掉 static,然后啟動應用:

@Data
public class Session {  // 去掉 staticprivate String key;private String date;
}

結果

  • Spring Boot 啟動時會報錯,或 session 的屬性無法注入。
  • 使用 @Autowired CaptchaProperties 時,session 可能是 null

4. 最佳實踐


? 推薦方式

保持 static,這是 Spring Boot 配置綁定的標準做法:

@ConfigurationProperties(prefix = "captcha")
@Data
public class CaptchaProperties {private Integer width;private Integer height;private Session session;  // 依賴靜態內部類@Datapublic static class Session {  // 必須是 staticprivate String key;private String date;}
}

? 替代方案(不推薦)

如果因為某些原因你不想用靜態內部類,可以用 獨立的類

// 單獨定義 Session 類
@Data
public class SessionConfig {private String key;private String date;
}// 在 CaptchaProperties 中引用
@ConfigurationProperties(prefix = "captcha")
@Data
public class CaptchaProperties {private Integer width;private Integer height;private SessionConfig session;  // 使用獨立類
}

但這樣會讓代碼更分散,不如靜態內部類簡潔。


5. 總結

方案是否推薦原因
靜態內部類 (static class)? 推薦Spring Boot 配置綁定的標準方式,簡潔且無副作用。
非靜態內部類 (去掉 static)? 不推薦Spring 無法綁定嵌套屬性,session 會為 null
獨立類?? 可用可行,但會讓配置類分散,不如靜態內部類直觀。

因此,在你的場景中必須加 static,否則配置無法正確綁定!


6. 重新調整向 Seisson 中設置驗證碼和生成時間的代碼


在這里插入圖片描述

1. 問題:用戶A和用戶B同時獲取驗證碼,會互相影響嗎?

情景步驟圖:用戶A和用戶B同時獲取驗證碼,是否互相影響?

用戶A和用戶B并發獲取驗證碼流程(含Session/Cookie機制)

用戶A的瀏覽器 用戶B的瀏覽器 服務器 用戶A首次請求驗證碼 GET /getCaptcha (無Cookie) 創建新Session(SessionID=123) 生成驗證碼"ABCD",存入session123 HTTP響應(Set-Cookie: JSESSIONID=123) 存儲Cookie: JSESSIONID=123 用戶B同時請求驗證碼 GET /getCaptcha (無Cookie) 創建新Session(SessionID=456) 生成驗證碼"WXYZ",存入session456 HTTP響應(Set-Cookie: JSESSIONID=456) 存儲Cookie: JSESSIONID=456 用戶A提交驗證 POST /check?captcha=ABCD\n(Cookie: JSESSIONID=123) 根據JSESSIONID=123查找Session 從session123讀取驗證碼"ABCD" 驗證成功(true) 用戶B提交驗證 POST /check?captcha=WXYZ\n(Cookie: JSESSIONID=456) 根據JSESSIONID=456查找Session 從session456讀取驗證碼"WXYZ" 驗證成功(true) 關鍵點說明: 1. 每個新會話都會創建獨立Session 2. Set-Cookie只在首次響應時發送 3. 瀏覽器自動維護各自的Cookie 4. Session數據完全隔離 用戶A的瀏覽器 用戶B的瀏覽器 服務器

流程關鍵點解析

  1. Session創建時機

    • 當瀏覽器首次訪問且無JSESSIONID Cookie時,服務器會立即創建新Session
    • 創建時會生成唯一SessionID(示例中123和456)
  2. Set-Cookie機制

    • 只在首次響應時通過Set-Cookie頭下發JSESSIONID
    • 瀏覽器后續請求會自動攜帶該Cookie
  3. 數據隔離原理

    用戶SessionID存儲的驗證碼使用的Cookie
    用戶A123ABCDJSESSIONID=123
    用戶B456WXYZJSESSIONID=456
  4. 驗證過程

    • 服務器始終根據請求中的JSESSIONID值查找對應Session
    • 不同用戶的Session存儲空間完全獨立

為什么不會互相影響?

  • Cookie隔離:瀏覽器之間不會共享Cookie
  • 服務端Session隔離:SessionID不同導致數據存儲位置不同
  • 自動關聯機制:Spring自動通過Cookie中的JSESSIONID關聯對應Session

即使key名稱相同(如都叫"CAPTCHA_CODE"),但因存儲在不同的Session對象中,實際上相當于session123.get("CAPTCHA_CODE")session456.get("CAPTCHA_CODE")的區別。

情景復現(修正版)

  1. 用戶A 訪問 /getCaptcha

    • 服務器創建 SessionA,存驗證碼 CodeA,并返回 Set-Cookie: JSESSIONID=SessionA
    • 用戶A的瀏覽器保存這個 Cookie,之后的請求都會帶上 JSESSIONID=SessionA
  2. 用戶B 訪問 /getCaptcha

    • 服務器創建 SessionB,存驗證碼 CodeB,并返回 Set-Cookie: JSESSIONID=SessionB
    • 用戶B的瀏覽器保存這個 Cookie,之后的請求都會帶上 JSESSIONID=SessionB
  3. 用戶A 提交驗證碼(訪問 /check):

    • 瀏覽器自動帶上 JSESSIONID=SessionA
    • 服務器從 SessionA 里取驗證碼(CodeA),和用戶A輸入的驗證碼比較。
    • 不會讀到 SessionB 的內容!
  4. 用戶B 提交驗證碼(訪問 /check):

    • 瀏覽器自動帶上 JSESSIONID=SessionB
    • 服務器從 SessionB 里取驗證碼(CodeB),和用戶B輸入的驗證碼比較。
    • 不會讀到 SessionA 的內容!

2. 核心概念:Session 如何區分不同用戶?

  • Session 的本質:服務器為每個用戶創建的一個獨立存儲空間(類似一個私人保險箱)。
  • 如何區分不同用戶?:靠 Cookie(JSESSIONID),瀏覽器每次請求會自動帶上這個 Cookie,告訴服務器“我是誰”。

關鍵流程:

  1. 用戶第一次訪問網站(比如 /getCaptcha):

    • 服務器發現請求沒有 JSESSIONID Cookie,就創建一個新 Session,并生成一個唯一 ID(如 Session123)。
    • 服務器返回響應時,會通過 Set-Cookie 頭告訴瀏覽器:JSESSIONID=Session123
    • 瀏覽器之后每次請求都會自動帶上這個 Cookie。
  2. 用戶第二次請求(比如 /check):

    • 瀏覽器會自動在請求頭里帶上:Cookie: JSESSIONID=Session123
    • 服務器通過這個 JSESSIONID 找到對應的 Session(Session123),然后從里面讀取之前存的驗證碼。

3. 為什么不會混亂?

  • Session 的 key(CAPHCHA_SESSION_KEY)雖然一樣,但每個用戶的 Session 是獨立的。
    • 類似:你和朋友都有一個叫“密碼”的抽屜,但你們的抽屜在不同的保險箱里(SessionA vs SessionB),互不影響。
  • 瀏覽器通過 JSESSIONID 告訴服務器該用哪個 Session,所以服務器永遠不會搞混。

4. 你的代碼是安全的

  • check(String captcha, HttpSession session) 里的 session 參數,是由 Spring 自動從當前請求的 JSESSIONID 關聯的 Session 里獲取的,所以:
    • 用戶A的請求 → 拿到 SessionA → 取 CodeA
    • 用戶B的請求 → 拿到 SessionB → 取 CodeB
  • 即使 key 是固定的,Session 隔離保證了數據不會串

5. 如果真想讓驗證碼混亂,得怎么做?
要讓用戶A和用戶B的驗證碼互相影響,必須:

  1. 讓用戶A和用戶B的請求使用同一個 JSESSIONID(比如黑客篡改 Cookie)。
  2. 或者把驗證碼存在全局變量里(比如 static Map),而不是 Session 里。

但你的代碼沒問題,因為 Session 天然隔離用戶


總結

  • Session 靠 JSESSIONID Cookie 區分用戶,不同用戶的 Session 互不干擾。
  • 你的代碼是線程安全的,因為每個用戶的驗證碼存在自己的 Session 里。
  • 用戶A的驗證碼不會因為用戶B刷新而失效,因為服務器讀取的是用戶A自己的 Session。

希望這個解釋更清晰!如果還有疑問,可以繼續討論。 😊


12. check( ) 接口的傳參細節


參數 Session 是從哪里傳參的呢

只要我們打開 http://127.0.0.1:8080/captcha/check?captcha=u8hs 這個頁面,就會調用后端 check 接口,此時我們就需要通過 URL 手動對 check() 的參數 captcha 傳參 ,那還有一個參數 Session 是從哪里傳參的呢

在這里插入圖片描述


在訪問 http://127.0.0.1:8080/captcha/check?captcha=u8hs 時,Session 參數是 由 Spring 自動注入的,不需要手動傳參。以下是詳細說明:


Session 參數的來源


  • 自動注入機制
    Spring MVC 在調用控制器方法時,如果發現參數類型是 HttpSession,會 自動從當前請求中獲取或創建 Session,無需手動傳遞。

  • public boolean check(String captcha, HttpSession session) { ... }
    
    • captcha:通過 URL 參數 ?captcha=u8hs 傳遞。
    • session:由 Spring 從請求的 Cookie 中讀取 JSESSIONID 關聯的 Session(若無則新建)。

  • Session 的關聯性
    確保兩次請求(/getCaptcha/check)的 JSESSIONID 相同,否則會找不到之前存儲的驗證碼。可通過以下方式檢查:

    @RequestMapping("/check")
    public boolean check(String captcha, HttpSession session) {System.out.println("當前 Session ID: " + session.getId()); // 打印 Session ID// ...其他邏輯
    }
    

調整前端頁面代碼


2. 解決前端多次刷新頁面圖片生成不同步問題


在這里插入圖片描述

出現上述問題的原因是,我們的瀏覽器存在一些緩存?

🔍 問題現象分析

  1. 首次加載頁面

    • 瀏覽器請求 /captcha/getCaptcha,返回驗證碼圖片(狀態碼 200)。
    • 圖片被瀏覽器緩存(因為 GET 請求默認可緩存)。
  2. 第一次刷新頁面

    • 瀏覽器發現緩存中的圖片未過期(根據響應頭 Cache-ControlExpires),直接使用緩存(狀態碼 304 Not Modified)。
    • 驗證碼圖片未變化(因為未真正請求后端)。
  3. 第二次刷新頁面

    • 可能因緩存策略(如 max-age=0)或手動強制刷新(Ctrl+F5),瀏覽器重新請求服務器(狀態碼 200)。
    • 此時后端生成新的驗證碼,圖片變化。

📌 你的理解修正

你的說法修正/補充說明
“GET 請求是冪等的”? 正確,GET 是冪等的(多次請求不影響資源狀態)。
“瀏覽器會對未變化的資源緩存”? 正確,但需要明確是 根據響應頭決定是否緩存(如 Cache-Control)。
“第二次刷新不發送 GET 請求”? 不完全正確:瀏覽器會發送請求,但可能返回 304(協商緩存),而非完全不發送。

🛠? 解決方案


1. 禁用瀏覽器緩存(推薦)

getCaptcha 方法中添加響應頭,禁止緩存:

@RequestMapping("/getCaptcha")
public void getCaptcha(HttpServletResponse response, HttpSession session) throws IOException {// 禁用緩存response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");response.setHeader("Pragma", "no-cache");response.setDateHeader("Expires", 0);// 生成驗證碼邏輯...ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(...);// ...
}

2. 強制每次請求更新(前端修改)

在圖片 URL 后添加時間戳參數(避免緩存):

<img id="verificationCodeImg" src="/captcha/getCaptcha?dt=<%=new Date().getTime()%>" onclick="this.src='/captcha/getCaptcha?dt=' + new Date().getTime()" />

3. 修復前端代碼問題

你的前端代碼中,點擊事件寫錯了路徑(/admin/captcha 應改為 /captcha/getCaptcha):

$("#verificationCodeImg").click(function(){$(this).hide().attr('src', '/captcha/getCaptcha?dt=' + new Date().getTime()).fadeIn();
});

在這里插入圖片描述


💡 關鍵點總結

  1. GET 請求默認緩存:瀏覽器會緩存 GET 請求的響應(除非顯式禁用)。
  2. 304 狀態碼:表示資源未修改,瀏覽器使用本地緩存。
  3. 解決方案
    • 后端:通過響應頭禁用緩存。
    • 前端:添加隨機參數(如時間戳)繞過緩存。

? 最終效果

  • 每次訪問 /captcha/getCaptcha 都會生成新驗證碼。
  • 圖片不會因緩存而重復顯示舊驗證碼。

對于上述禁用緩存策略,我們采取修改后端的方法:

在這里插入圖片描述

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

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

相關文章

day16 學習筆記

文章目錄 前言一、廣播機制二、數組遍歷1.for循環2.nditer函數 三、數組操作1.reshape函數2.flat屬性3.flatten函數4.revel函數5.數組轉置6.升維與降維7.數組的連接與分割8.數組運算 前言 通過今天的學習&#xff0c;我進一步掌握了更多numpy的語法知識 一、廣播機制 廣播&am…

使用FastExcel時的單個和批量插入的問題

在我們用excel表進行插入導出的時候&#xff0c;通常使用easyexcel或者FastExcel&#xff0c;而fastexcel是easy的升級版本&#xff0c;今天我們就對使用FastExcel時往數據庫插入數據的業務場景做出一個詳細的剖析 場景1 現在我們數據庫有一張組織表&#xff0c;組織表的字段…

Cannot find a valid baseurl for repo: centos-sclo-sclo/x86_64

? rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-latest-5.0.el7.noarch.rpmyum clean allyum macache fast? 編輯配置文件 /etc/yum.repos.d/zabbix.repo and enable zabbix-frontend repository. [zabbix-frontend]...enabled1... 下載相關…

AI基礎02-圖片數據采集

上篇文章我們學習了文本的數據采集&#xff0c;今天主要了解一下圖片數據采集的方法。圖片采集方法通常有網頁采集和實時采集&#xff08;傳感器采集&#xff09;兩種。我們學習一下如何利用python 工具和筆記本計算機攝像頭進行圖片數據的實時采集。 1&#xff09;cv2庫簡介 …

【CSS】相對位置小練習

要求&#xff1a; 成果&#xff1a; 代碼&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>相對位置小練習</title><link rel"stylesheet" href"./css/style.css…

外設的中斷控制

如ADC、SPI、I2C、TIM等使用STM32 HAL庫時的中斷函數調用方式和UART非常類似&#xff0c;都有底層直接使能中斷和上層庫函數管理兩種方式。下面詳細說明幾種典型外設&#xff1a; 一、ADC外設 &#xff08;1&#xff09;直接使能中斷&#xff08;底層控制&#xff09;&#xf…

網絡傳輸優化之多路復用與解復用

一、基本概念 多路復用 發送端將來自多個應用或進程的數據流合并到同一物理信道中傳輸的過程。核心目的是提高信道利用率&#xff0c;減少資源浪費。例如&#xff0c;多個網絡應用&#xff08;如瀏覽器、郵件客戶端&#xff09;通過不同端口將數據封裝為報文段&#xff0c;共享…

【軟考-架構】10.1、軟件工程概述-CMM-軟件過程模型-逆向工程

?資料&文章更新? GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目錄 軟件工程基礎知識軟件工程概述能力成熟度模型能力成熟度模型CMM能力成熟度模型集成CMMI &#x1f4af;考試真題第一題第二題 軟件過程模型瀑布模型&#xff08;SDLC&#…

python將整個txt文件寫入excel的一個單元格?

要將整個txt文件寫入Excel的一個單元格&#xff0c;可以使用Python的openpyxl庫來實現。以下是一個簡單的示例代碼&#xff1a; from openpyxl import Workbook# 讀取txt文件內容 with open(file.txt, r) as file:txt_content file.read()# 創建一個新的Excel工作簿 wb Work…

車載以太網網絡測試 -25【SOME/IP-報文格式-1】

1 摘要 本專題接著上一專題對SOME/IP進行介紹&#xff0c;主要對SOME/IP報文格式以及定義的字段進行詳細介紹&#xff0c;有助于在實際項目過程中對SOME/IP報文的理解。 上文回顧&#xff1a; 車載以太網網絡測試 -24【SOME/IP概述】 2 SOME/IP-報文格式 通過上個專題介紹&a…

【區塊鏈安全 | 第五篇】DeFi概念詳解

文章目錄 DeFi1. DeFi 生態概覽2. 去中心化交易所&#xff08;DEX&#xff09;2.1 AMM&#xff08;自動做市商&#xff09;模型2.2 訂單簿模式&#xff08;現貨交易&#xff09; 3. 借貸協議3.1 Aave3.2 使用閃電貸&#xff08;Flash Loan&#xff09; 4. 穩定幣&#xff08;St…

問題:md文檔轉換word,html,圖片,excel,csv

文章目錄 問題&#xff1a;md文檔轉換word&#xff0c;html&#xff0c;圖片&#xff0c;excel&#xff0c;csv&#xff0c;ppt**主要職責****技能要求****發展方向****學習建議****薪資水平** 方案一&#xff1a;AI Markdown內容轉換工具打開網站md文檔轉換wordmd文檔轉換pdfm…

代碼隨想錄刷題day53|(二叉樹篇)106.從中序與后序遍歷序列構造二叉樹(▲

目錄 一、二叉樹理論知識 二、構造二叉樹思路 2.1 構造二叉樹流程&#xff08;給定中序后序 2.2 整體步驟 2.3 遞歸思路 2.4 給定前序和后序 三、相關算法題目 四、易錯點 一、二叉樹理論知識 詳見&#xff1a;代碼隨想錄刷題day34|&#xff08;二叉樹篇&#xff09;二…

前端知識點---用正則表達式判斷郵箱(javascript)

// 全面的正則&#xff08;兼容大多數情況&#xff09; const emailRegex /^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$/;// 或直接使用瀏覽器內置驗證 <input type"email" required>/&#xff1a;正則表達式的起始和結束標志。 ^&#xff1a;匹配字符串的…

PyQt6實例_批量下載pdf工具_界面開發

目錄 前置&#xff1a; 代碼&#xff1a; 視頻&#xff1a; 前置&#xff1a; 1 本系列將以 “PyQt6實例_批量下載pdf工具”開頭&#xff0c;放在 【PyQt6實例】 專欄 2 本系列涉及到的PyQt6知識點&#xff1a; 線程池&#xff1a;QThreadPool,QRunnable&#xff1b; 信號…

在word中使用zotero添加參考文獻并附帶超鏈接

一、引言 在寫大論文時&#xff0c;為了避免文中引用與文末參考文獻頻繁對照、修改文中引用順序/引用文獻時手動維護參考文獻耗易出錯&#xff0c;擬在 word 中使用 zotero 插入參考文獻&#xff0c;并為每個參考文獻附加超鏈接&#xff0c;實現交互式閱讀。 版本&#xff1a…

Selenium文件上傳

在 Web 自動化測試中,文件上傳是一項常見的任務。不同的網站和前端技術可能導致上傳方式有所不同,因此需要采用不同的方法進行處理。 方法 1:使用 send_keys() 直接上傳(最常用) 適用場景: 頁面中 有標準的 <input type="file"> 標簽。 不需要彈出 Wind…

線程概念與控制(中)

線程概念與控制&#xff08;上&#xff09;https://blog.csdn.net/Small_entreprene/article/details/146464905?sharetypeblogdetail&sharerId146464905&sharereferPC&sharesourceSmall_entreprene&sharefrommp_from_link我們經過上一篇的學習&#xff0c;接…

【Unity】 鼠標拖動物體移動速度跟不上鼠標,會掉落

錯誤示范&#xff1a; 一開始把移動的代碼寫到update里去了&#xff0c;發現物體老是掉(總之移動非常不流暢&#xff0c;體驗感很差&#xff09; void Update(){Ray ray Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out RaycastHit hit, M…

MATLAB 控制系統設計與仿真 - 30

用極點配置設計伺服系統 方法2-反饋修正 如果我們想只用前饋校正輸入&#xff0c;從而達到伺服控制的效果&#xff0c;我們需要很精確的知道系統的參數模型&#xff0c;否則系統輸出仍然具有較大的靜態誤差。 但是如果我們在誤差比較器和系統的前饋通道之間插入一個積分器&a…