【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.mzph.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…