news 2026/5/25 10:16:07

springboot 分布式验证码登录的通用方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
springboot 分布式验证码登录的通用方案

为了防止世界被破坏,为了守护世界的和平。。。说错了,重来~

为了防止验证系统被暴力破解,很多系统都增加了验证码效验,比较常见的就是图片二维码,业内比较安全的是短信验证码,当然还有一些拼图验证码,加入人工智能的二维码等等,我们今天的主题就是前后端分离的图片二维码登录方案。

前后端未分离的验证码登录方案

传统的项目大都是基于session交互的,前后端都在一个项目里面,比如传统的SSH项目或者一些JSP系统,当前端页面触发到获取验证码请求,可以将验证码里面的信息存在上下文中,所以登录的时候只需要用户名密码验证码即可。

验证码生成流程如下

登录验证流程如下

可以发现,整个登录流程还是依赖session上下文的,并且由后端调整页面。

前后端分离的验证码登录方案

随着系统和业务的不停升级,前后端代码放在一起的项目越来越臃肿,已经无法快速迭代和职责区分了,于是纷纷投入了前后端分离的怀抱,发现代码和职责分离以后,开发效率越来越高了,功能迭代还越来越快,但是以前的验证码登录方案就要更改了。

验证码生成流程如下

对比原来的方案,增加了redis中间件,不再是存在session里面了,但是后面怎么区分这个验证码是这个请求生成的呢?所以我们加入了唯一标识符来区分

登录验证流程如下

可以发现,基于前后端分离的分布式项目登录方案对比原来,加了一个redis中间件和token返回,不再依赖上下文session,并且页面调整也是由后端换到了前端

动手撸轮子

基于验证码的轮子还是挺多的,本文就以Kaptcha这个项目为例,通过springboot项目集成Kaptcha来实现验证码生成和登录方案。

Kaptcha介绍

Kaptcha是一个基于SimpleCaptcha的验证码开源项目

我找的这个轮子是基于SimpleCaptcha二次封装的,maven依赖如下

<!--Kaptcha是一个基于SimpleCaptcha的验证码开源项目--> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency>

新建项目并加入依赖

依赖主要有 SpringBoot、Kaptcha、Redis

pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lzp</groupId> <artifactId>kaptcha</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> <relativePath/><!-- lookup parent from repository --> </parent> <dependencies> <!--Kaptcha是一个基于SimpleCaptcha的验证码开源项目--> <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redis依赖commons-pool 这个依赖一定要添加 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Redis配置类RedisConfig
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } }
验证码配置类KaptchaConfig
@Configuration publicclass KaptchaConfig { @Bean public DefaultKaptcha producer(){ DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); Properties properties = new Properties(); properties.setProperty("kaptcha.border", "no"); properties.setProperty("kaptcha.border.color", "105,179,90"); properties.setProperty("kaptcha.textproducer.font.color", "black"); properties.setProperty("kaptcha.image.width", "110"); properties.setProperty("kaptcha.image.height", "40"); properties.setProperty("kaptcha.textproducer.char.string","23456789abcdefghkmnpqrstuvwxyzABCDEFGHKMNPRSTUVWXYZ"); properties.setProperty("kaptcha.textproducer.font.size", "30"); properties.setProperty("kaptcha.textproducer.char.space","3"); properties.setProperty("kaptcha.session.key", "code"); properties.setProperty("kaptcha.textproducer.char.length", "4"); properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); // properties.setProperty("kaptcha.obscurificator.impl","com.xxx");可以重写实现类 properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise"); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; }
验证码控制层CaptchaController

为了方便代码写一块了,讲究看

package com.lzp.kaptcha.controller; import com.google.code.kaptcha.impl.DefaultKaptcha; import com.lzp.kaptcha.service.CaptchaService; import com.lzp.kaptcha.vo.CaptchaVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import sun.misc.BASE64Encoder; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; @RestController @RequestMapping("/captcha") publicclass CaptchaController { @Autowired private DefaultKaptcha producer; @Autowired private CaptchaService captchaService; @ResponseBody @GetMapping("/get") public CaptchaVO getCaptcha() throws IOException { // 生成文字验证码 String content = producer.createText(); // 生成图片验证码 ByteArrayOutputStream outputStream = null; BufferedImage image = producer.createImage(content); outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "jpg", outputStream); // 对字节数组Base64编码 BASE64Encoder encoder = new BASE64Encoder(); String str = "data:image/jpeg;base64,"; String base64Img = str + encoder.encode(outputStream.toByteArray()).replace("\n", "").replace("\r", ""); CaptchaVO captchaVO =captchaService.cacheCaptcha(content); captchaVO.setBase64Img(base64Img); return captchaVO; } }
验证码返回对象CaptchaVO
package com.lzp.kaptcha.vo; publicclass CaptchaVO { /** * 验证码标识符 */ private String captchaKey; /** * 验证码过期时间 */ private Long expire; /** * base64字符串 */ private String base64Img; public String getCaptchaKey() { return captchaKey; } public void setCaptchaKey(String captchaKey) { this.captchaKey = captchaKey; } public Long getExpire() { return expire; } public void setExpire(Long expire) { this.expire = expire; } public String getBase64Img() { return base64Img; } public void setBase64Img(String base64Img) { this.base64Img = base64Img; } }
Redis封装类RedisUtils

网上随意找的,类里面注明来源,将就用,代码较多就不贴了,文末有代码获取

验证码方法层CaptchaService
package com.lzp.kaptcha.service; import com.lzp.kaptcha.utils.RedisUtils; import com.lzp.kaptcha.vo.CaptchaVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.UUID; @Service publicclass CaptchaService { @Value("${server.session.timeout:300}") private Long timeout; @Autowired private RedisUtils redisUtils; privatefinal String CAPTCHA_KEY = "captcha:verification:"; public CaptchaVO cacheCaptcha(String captcha){ //生成一个随机标识符 String captchaKey = UUID.randomUUID().toString(); //缓存验证码并设置过期时间 redisUtils.set(CAPTCHA_KEY.concat(captchaKey),captcha,timeout); CaptchaVO captchaVO = new CaptchaVO(); captchaVO.setCaptchaKey(captchaKey); captchaVO.setExpire(timeout); return captchaVO; } }
用户登录对象封装LoginDTO
package com.lzp.kaptcha.dto; publicclass LoginDTO { private String userName; private String pwd; private String captchaKey; private String captcha; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getCaptchaKey() { return captchaKey; } public void setCaptchaKey(String captchaKey) { this.captchaKey = captchaKey; } public String getCaptcha() { return captcha; } public void setCaptcha(String captcha) { this.captcha = captcha; } }
登录控制层UserController

这块我写逻辑代码了,相信大家都看的懂

package com.lzp.kaptcha.controller; import com.lzp.kaptcha.dto.LoginDTO; import com.lzp.kaptcha.utils.RedisUtils; import com.lzp.kaptcha.vo.UserVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") publicclass UserController { @Autowired private RedisUtils redisUtils; @PostMapping("/login") public UserVO login(@RequestBody LoginDTO loginDTO) { Object captch = redisUtils.get(loginDTO.getCaptchaKey()); if(captch == null){ // throw 验证码已过期 } if(!loginDTO.getCaptcha().equals(captch)){ // throw 验证码错误 } // 查询用户信息 //判断用户是否存在 不存在抛出用户名密码错误 //判断密码是否正确,不正确抛出用户名密码错误 //构造返回到前端的用户对象并封装信息和生成token returnnew UserVO(); } }

验证码获取和查看

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

Java毕设项目推荐-基于springboot的汽车租赁买卖管理系统的设计与实现租赁与买卖二手车交易【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/25 16:23:02

【课程设计/毕业设计】基于springboot的影院购票管理系统的设计与实现基于 SpringBoot 的电影院购票系统设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/26 7:18:33

如何正确删除电脑的缓存文件?

新的电脑总是好的&#xff0c;各种干净整洁无垃圾。 还是新的好 表情包 使用了一段时间之后&#xff0c;小伙伴们就会发现电脑C盘飙红了。然后就各种论坛查找清除电脑垃圾的方法。 电脑正常使用下&#xff0c;是会产生很多缓存的&#xff0c;所以C盘红了也很正常。除非电脑组…

作者头像 李华
网站建设 2026/5/25 16:28:23

[python] 代码性能分析工具line_profiler使用指北

码分析能够评估各部分代码的时间消耗&#xff0c;即进行时间复杂度分析。通过这一过程&#xff0c;我们可以识别影响整体运行效率的关键部分&#xff0c;从而更高效地利用底层计算资源。此外&#xff0c;代码分析也可用于评估内存使用情况&#xff0c;即空间复杂度&#xff0c;…

作者头像 李华
网站建设 2026/5/26 3:14:22

《手搓》线程池

一、什么是《手搓》线程池手搓线程池并不是用来完全代替系统线程池的你可以把手搓线程池看做系统线程池的一部分就好比在东海用集装箱搞养殖一个集装箱里养鱼另一个集装箱里养虾搞好隔离,鱼虾都不耽搁二、最常用线程池的场景是什么当然是Task,是用TaskFactory.StartNew方法创建…

作者头像 李华