Springboot 分布式验证码登录的通用方案
传统的项目大都是基于session交互的,前后端都在一个项目里面,比如传统的SSH项目或者一些JSP系统,当前端页面触发到获取验证码请求,可以将验证码里面的信息存在上下文中,所以登录的时候只需要 用户名、密码、验证码即可。
为了防止世界被破坏,为了守护世界的和平。。。说错了,重来~
为了防止验证系统被暴力破解,很多系统都增加了验证码效验,比较常见的就是图片二维码,业内比较安全的是短信验证码,当然还有一些拼图验证码,加入人工智能的二维码等等,我们今天的主题就是前后端分离的图片二维码登录方案。
前后端未分离的验证码登录方案
传统的项目大都是基于session交互的,前后端都在一个项目里面,比如传统的SSH项目或者一些JSP系统,当前端页面触发到获取验证码请求,可以将验证码里面的信息存在上下文中,所以登录的时候只需要用户名、密码、验证码即可。
验证码生成流程如下
图片
登录验证流程如下
图片
可以发现,整个登录流程还是依赖session上下文的,并且由后端调整页面。
前后端分离的验证码登录方案
随着系统和业务的不停升级,前后端代码放在一起的项目越来越臃肿,已经无法快速迭代和职责区分了,于是纷纷投入了前后端分离的怀抱,发现代码和职责分离以后,开发效率越来越高了,功能迭代还越来越快,但是以前的验证码登录方案就要更改了。
验证码生成流程如下
图片
对比原来的方案,增加了redis中间件,不再是存在session里面了,但是后面怎么区分这个验证码是这个请求生成的呢?所以我们加入了唯一标识符来区分
登录验证流程如下
图片
可以发现,基于前后端分离的分布式项目登录方案对比原来,加了一个redis中间件和token返回,不再依赖上下文session,并且页面调整也是由后端换到了前端
动手撸轮子
基于验证码的轮子还是挺多的,本文就以Kaptcha这个项目为例,通过springboot项目集成Kaptcha来实现验证码生成和登录方案。
Kaptcha介绍
Kaptcha是一个基于SimpleCaptcha的验证码开源项目;
我找的这个轮子是基于SimpleCaptcha二次封装的,maven依赖如下:
新建项目并加入依赖
依赖主要有 SpringBoot、Kaptcha、Redis;
pom.xml
Redis配置类RedisConfig
@Configurationpublic class RedisConfig { @Bean public RedisTemplate
验证码配置类KaptchaConfig
@Configurationpublicclass 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("
", "").replace("
", ""); 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;@Servicepublicclass 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(); }}
验证码获取和查看
图片
相关攻略
AI智能体规模化应用面临架构瓶颈,用户多设备体验割裂。研究指出,分布式智能与系统级编排是破局关键,能大幅降低云端成本并提升响应与隐私安全。智能手机将演变为个人AI生态核心,行业需推动跨设备协同与边缘计算整合,实现以用户为中心的智能服务。
在人工智能与复杂系统研究的前沿,基于多智能体系统的分布式优化正成为核心技术焦点。它不仅引领着学术探索的方向,更是破解未来大规模协同与决策难题的关键。本文将深入解析其原理、方法与未来趋势。 一、研究背景与核心价值 我们已全面进入万物互联的时代。从工业物联网到智慧城市管理,系统的规模与复杂性呈指数级增长
在分布式智能体系统的设计与实践中,实现多个智能体(Agent)之间的高效通信与协同协作,是保障系统整体性能与可靠性的关键技术。这好比一支高度协同的团队,若成员间缺乏顺畅的信息交换与任务配合,即便个体能力再强,也难以达成整体目标。那么,这些分布在网络各处的智能体,究竟通过哪些机制与策略来完成有效的“对
分布式能源并网审批因申请激增而周期延长,核心瓶颈在于人工处理现场采集数据耗时严重。当前焦点转向利用实景捕获与计算机视觉技术,自动从图像生成工程数据,以压缩准备时间。电力公司可通过优化流程、引入自动化工具提升效率,从而加速并网进程。
在分布式系统中,多个智能体(Agent)如何协同完成共同目标,是提升系统效率的关键课题。这就像一支无需指挥的交响乐团,每个成员自主决策却又和谐统一。实现这种高效协作,依赖于一系列精心设计的核心运行机制。 分布式决策:局部感知与全局优化 每个Agent都具备独立的决策能力。它们基于自身感知的局部状态与
热门专题
热门推荐
在《燕云十六声》中领悟“菩提苦海”,需沉浸探索游戏世界。主线剧情构建认知框架,战斗观察、场景细节与NPC对话皆暗藏线索。通过多元视角拼凑因果,方能深入理解游戏蕴含的宏大叙事与深邃魅力。
2026年618大促的序幕刚刚拉开,初期战报已经透露出一些耐人寻味的信号。截至5月21日,海信电视在京东平板电视累计销售竞速榜上拔得头筹,其RGB-Mini LED爆款王——海信小墨E5S Pro,更是同时拿下了天猫平板电视和抖音大家电的5 20单品销冠。 这并非偶然。奥维云网的全渠道监测数据给出了
充电桩领域的“军备竞赛”再次迎来重磅升级。5月22日,极氪汽车正式发布了其全新一代液冷超级充电桩,将单枪峰值功率一举提升至行业领先的800kW,标志着超充技术迈入新阶段。 根据官方披露的核心信息,这款超充桩主要具备四大优势:极速补能、高效节能、广泛适配与多重安全。具体而言,其单枪峰值电流高达800A
获取电弧机剑主要有五种途径:推进主线任务以解锁线索;探索遗迹、工厂等特定区域;挑战特定副本与Boss;完成提及传说武器或遗物的支线任务;参与限时活动并达成要求。玩家可根据偏好选择或组合多种方式获取该武器。
小米汽车再次为潜在车主带来惊喜福利!即日起至5月31日,用户只需提前完成预约,并到店参与任意车型的试驾体验,即可免费获赠一款1:64精致合金车模。车模款式与颜色随机发放,为试驾过程增添一份专属的收藏乐趣,诚意十足。 参与本次活动需注意以下细则:试驾必须通过官方渠道提前预约;各授权门店的车模备货数量不





