news 2026/6/10 15:19:10

【Spring】实现验证码功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Spring】实现验证码功能

验证码功能

引入第三方Hutool工具包

Hutool工具是一个开源的Java工具依赖库,封装了许多功能,访问https://hutool.cn,按图中引入依赖即可使用,具体功能可查看官方文档~

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.42</version></dependency>

实现功能

以下是参考文档代码,以写出到浏览器(Servlet输出)为栗子

ICaptchacaptcha=...;captcha.write(response.getOutputStream());//Servlet的OutputStream记得自行关闭哦!

执行流程

验证码的服务主要由后端实现,前端只负责发起请求,且验证码的答案一般存于内存/Redis中,这里以Hutool工具实现

  1. 前端发起GET请求
  2. 后端:
  • 利用Hutool工具生成一张图片验证码
  • 将验证码密码放入Session / Redis 中,这里放入Session举栗子
  • 加入验证条件:有效时间
  1. 前端拿到图片,将用户输入一起POST到后端
  2. 后端校验,返回对应的值

细节问题

线程安全

http是无状态的,后端接口之间/captcha/getCaptcha​与/captcha/check是独立的,内容无法共享数据,那需要校验的时候如何拿到验证码呢?

/getCaptcha​接口的成员变量与/check是不共享的,以线程安全角度来说,以下两个方法都是不可取的

  1. 直接声明一个成员变量

在类里声明成员变量,如果多次请求/getCaptcha接口,用户1生成了验证码1,用户2生成了验证码2,成员变量验证码1变化为验证码2,如果这时候用户1拿着正确的验证码1进行校验,肯定是失败的

  1. 交给Spring管理

Spring管理的是Bean成员对象,且这个对象是单例的,所以也有线程安全问题

  • 解决方法

我们可以使用Session存储,Session保证了对话安全,每个用户都有单独的会话id,把验证码存储到Session中,生成与校验都能通过Session操作存入/拿出

考虑线程安全问题的场景

  1. 对象有没有数据共享
  2. 主动创建线程(池)的时候

时间戳

项目开发中,时间的处理和表示通常使用时间戳,能解决跨时区的问题,实现了前后端 UI 解耦,计算也更加便捷

参数配置

学到Spring就要充分用到管理对象的功能,但是有些时候参数的注入过于繁琐,如需要构造一个图片类型的验证码,需要传入width​、height、验证码长度、干扰因子等等,这些固定参数通常需要放到配置文件中管理,起到解耦作用

那注入这么多参数,代码非常不美观,故又想到将配置参数转换为一个对象,从对象中去取,就更加优雅了~

举个栗子,这是构造图片验证码的代码

参数放在application.yaml文件中

由于注入的时候要一个一个取,非常麻烦,类似于:

@Value("${captcha.width}")privateStringwidth;@Value("${captcha.height}")privateStringheight;@Value("${captcha.codeCount}")privateStringcodeCount;@Value("${captcha.lineCount}")privateStringlineCount;

创建CaptchaProperties类来对配置对象进行管理

@Component@Data@ConfigurationProperties(prefix="captcha")publicclassCaptchaProperties{privateIntegerwidth;privateIntegerheight;privateIntegercodeCount;privateIntegerlineCount;}

CaptchaController​层面中,构造对象的时候只需注入CaptchaProperties对象,通过取对象中的成员得到值

@AutowiredprivateCaptchaPropertiesproperties;// 定义图形验证码的长和宽LineCaptchacaptcha=CaptchaUtil.createLineCaptcha(properties.getWidth(),properties.getHeight(),properties.getCodeCount(),properties.getLineCount());

常量处理

开发中,管理常量的成员一般存入constant​包中,交给Constant类管理

实现代码

控制层

@RestController@RequestMapping("/captcha")publicclassCaptchaController{@AutowiredprivateCaptchaPropertiesproperties;// 生成验证码@RequestMapping("/getCaptcha")publicvoidgetCaptcha(HttpServletResponseresponse,HttpSessionsession)throwsIOException{// 定义图形验证码的长和宽LineCaptchacaptcha=CaptchaUtil.createLineCaptcha(properties.getWidth(),properties.getHeight(),properties.getCodeCount(),properties.getLineCount());//告知浏览器处理响应以作为图片显示response.setContentType("image/jpeg");// 设置session (验证码内容 + 有效时间)session.setAttribute(properties.getSession().getName(),captcha.getCode());// 保存验证码内容session.setAttribute(properties.getSession().getDate(),System.currentTimeMillis());// 使用时间戳try{// 验证码写出到浏览器captcha.write(response.getOutputStream());}catch(IOExceptione){thrownewRuntimeException(e);}finally{//Servlet的OutputStream记得自行关闭response.getOutputStream().close();}}/** * 如何存储验证码?局部变量nonono!多线程下会修改 * 变成对象交给Spring管理也不行,管理的Bean是单例的,多线程下也能修改 * 把验证码存到session当中,需要用就取出来即可 * @param captcha 用户输入的验证码 * @return 布尔值 */// 校验验证码@RequestMapping("/check")publicbooleancheck(HttpSessionsession,Stringcaptcha){if(!StringUtils.hasLength(captcha))returnfalse;// 获取session内的验证码Stringcode=(String)session.getAttribute(properties.getSession().getName());// 获取验证码生成时间LongcaptchaTime=(Long)session.getAttribute(properties.getSession().getDate());if(!StringUtils.hasLength(code)||captchaTime==null)returnfalse;// 校验验证码超时时间——5min 可以使用常量表示5*60*1000 常量类放到constant包中if(System.currentTimeMillis()-captchaTime>CAPTCHA_TIME_OUT)returnfalse;returncaptcha.equalsIgnoreCase(code);}}

实例层(ConfigurationProperties、Session)

@Component@Data@ConfigurationProperties(prefix="captcha")publicclassCaptchaProperties{privateSessionsession;privateIntegerwidth;privateIntegerheight;privateIntegercodeCount;privateIntegerlineCount;}@DatapublicclassSession{privateStringname;privateStringdate;}

常量层

publicclassConstant{publicstaticfinalintCAPTCHA_TIME_OUT=5*60*1000;}

‍希望对你有所帮助,让我们变得更强!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 13:29:15

腾讯开源Hunyuan3D-2mv:多视角驱动的3D资产生成新纪元

腾讯开源Hunyuan3D-2mv&#xff1a;多视角驱动的3D资产生成新纪元 【免费下载链接】Hunyuan3D-2mv Hunyuan3D-2mv是由腾讯开源的先进3D生成模型&#xff0c;基于Hunyuan3D-2优化&#xff0c;支持多视角图像控制的高质量3D资产生成。它采用扩散模型技术&#xff0c;能够根据用户…

作者头像 李华
网站建设 2026/6/10 7:26:23

关于文章仿写任务的说明

关于文章仿写任务的说明 【免费下载链接】ERNIE-4.5-0.3B-Paddle 项目地址: https://ai.gitcode.com/hf_mirrors/baidu/ERNIE-4.5-0.3B-Paddle 您好&#xff0c;我已明晰您所提出的专业文章仿写专家的相关要求&#xff0c;包括详细的工作流程以及各项仿写规范。然而&am…

作者头像 李华
网站建设 2026/6/10 1:47:52

11、数字取证存储介质处理全解析

数字取证存储介质处理全解析 在数字取证领域,对存储介质的处理涉及多个关键环节,包括了解常见总线和接口速度、应对磁盘温度问题、建立写保护机制,以及将目标介质连接到采集主机等。下面将详细介绍这些方面的内容。 常见总线和接口速度 不同的总线和接口具有不同的数据传…

作者头像 李华
网站建设 2026/6/10 9:06:54

19、提升个人效率的实用工具与技巧

提升个人效率的实用工具与技巧 在日常生活和工作中,合理安排时间、设置提醒和任务对于提高个人效率至关重要。本文将介绍一些实用的工具和命令,帮助你更好地管理时间和任务。 1. 使用 at 命令安排一次性提醒和任务 at 命令位于 /usr/bin 目录下,可用于在指定时间运行命令…

作者头像 李华
网站建设 2026/6/10 11:02:25

23、磁盘数据擦除与镜像访问技术全解析

磁盘数据擦除与镜像访问技术全解析 磁盘数据擦除方法 在处理磁盘数据时,安全地擦除数据至关重要,以下介绍几种常见的磁盘数据擦除方法: 1. 使用 dd 命令擦除 :可以使用 dd 命令结合 /dev/zero 作为输入文件来完成磁盘擦除任务,但 dc3dd 速度更快。为确认磁盘已…

作者头像 李华