告别踩坑!若依图片验证码实现与改造指南

简介: 若依(RuoYi)框架中图片验证码的完整实现逻辑、核心代码和定制化方法,我会从实现原理、核心代码拆解、关键配置、定制化改造四个维度讲解,覆盖验证码生成、存储、校验全流程,让你既能理解原理,也能按需修改验证码样式 / 规则。

 若依(RuoYi)框架中图片验证码的完整实现逻辑、核心代码和定制化方法,我会从实现原理、核心代码拆解、关键配置、定制化改造四个维度讲解,覆盖验证码生成、存储、校验全流程,让你既能理解原理,也能按需修改验证码样式 / 规则。

若依最新前后端分离源码下载:

https://gitee.com/ruoyieleadmin/ruoyi-ele-admin

一、图片验证码核心实现原理

若依框架的图片验证码基于 Java 图形绘制 + Redis 存储 + 前端交互 实现,核心流程如下:

image.gif 编辑

核心关键点:

  • 验证码字符不直接返回前端,仅存储在 Redis,避免前端破解;
  • 验证码图片添加干扰线、噪点、扭曲变形,提升安全性;
  • 验证码设置短过期时间(默认 2 分钟),避免复用。

二、核心代码拆解(若依源码级解析)

若依的验证码功能集中在 ruoyi-common 模块的 captcha 包下,核心类如下:

1. 验证码生成核心类(CaptchaUtil.java)

负责生成随机字符、绘制验证码图片,是核心工具类:

java

package com.ruoyi.common.utils.captcha;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
/**
 * 若依框架验证码生成工具类
 */
public class CaptchaUtil {
    // 验证码字符集(排除易混淆字符:0/O、1/I等)
    private static final char[] CODE_CHAR = { '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
            'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
    // 验证码宽度/高度
    private static final int WIDTH = 110;
    private static final int HEIGHT = 38;
    // 验证码字符个数
    private static final int CODE_COUNT = 4;
    // 干扰线数量
    private static final int LINE_COUNT = 150;
    /**
     * 生成验证码图片+字符
     * @return CaptchaResult:包含验证码字符、图片BufferedImage
     */
    public static CaptchaResult generate() {
        // 1. 创建图片缓冲区
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        
        // 2. 设置背景色(白色)
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        // 设置字体(加粗、倾斜,增加识别难度)
        g.setFont(new Font("Arial", Font.BOLD | Font.ITALIC, 24));
        // 3. 生成随机验证码字符
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_COUNT; i++) {
            char c = CODE_CHAR[random.nextInt(CODE_CHAR.length)];
            code.append(c);
            // 绘制字符(随机颜色、位置、旋转角度)
            g.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200)));
            // 字符旋转(-30° ~ 30°)
            g.rotate(Math.toRadians(random.nextInt(60) - 30), 25 * i + 10, HEIGHT / 2);
            g.drawString(String.valueOf(c), 25 * i + 10, HEIGHT / 2);
            // 旋转复位
            g.rotate(-Math.toRadians(random.nextInt(60) - 30), 25 * i + 10, HEIGHT / 2);
        }
        // 4. 绘制干扰线(随机颜色、位置)
        for (int i = 0; i < LINE_COUNT; i++) {
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            g.drawLine(random.nextInt(WIDTH), random.nextInt(HEIGHT),
                       random.nextInt(WIDTH), random.nextInt(HEIGHT));
        }
        // 5. 绘制噪点(增加干扰)
        for (int i = 0; i < WIDTH * HEIGHT / 20; i++) {
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            g.fillRect(random.nextInt(WIDTH), random.nextInt(HEIGHT), 1, 1);
        }
        // 6. 释放资源
        g.dispose();
        // 返回结果:验证码字符、图片
        return new CaptchaResult(code.toString(), image);
    }
    /**
     * 验证码结果封装类
     */
    public static class CaptchaResult {
        private String code; // 验证码字符
        private BufferedImage image; // 验证码图片
        // 构造器、getter/setter
        public CaptchaResult(String code, BufferedImage image) {
            this.code = code;
            this.image = image;
        }
        // ... 省略getter/setter
    }
}

image.gif

2. 验证码接口实现(CaptchaController.java)

提供前端访问的验证码接口,生成图片并存储到 Redis:

java

package com.ruoyi.web.controller.common;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.captcha.CaptchaUtil;
import com.ruoyi.common.utils.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
 * 若依验证码接口控制器
 */
@RestController
public class CaptchaController {
    @Autowired
    private RedisCache redisCache;
    /**
     * 获取验证码接口
     * 前端访问:/captchaImage
     */
    @GetMapping("/captchaImage")
    public AjaxResult getCaptchaImage() {
        // 1. 生成验证码字符+图片
        CaptchaUtil.CaptchaResult captchaResult = CaptchaUtil.generate();
        String captchaCode = captchaResult.getCode();
        // 2. 生成验证码UUID(作为Redis的key)
        String uuid = UUID.randomUUID().toString().replace("-", "");
        // 3. 存入Redis,设置过期时间(2分钟)
        redisCache.setCacheObject("captcha_codes:" + uuid, captchaCode, 2, TimeUnit.MINUTES);
        
        // 4. 将图片转为Base64编码(返回前端展示)
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            ImageIO.write(captchaResult.getImage(), "jpg", outputStream);
            String imageBase64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
            // 5. 返回结果:UUID、Base64图片(前端用<img src="data:image/jpeg;base64,...">展示)
            return AjaxResult.success()
                    .put("uuid", uuid)
                    .put("img", imageBase64);
        } catch (Exception e) {
            return AjaxResult.error("验证码生成失败");
        }
    }
}

image.gif

3. 验证码校验逻辑(登录接口示例)

用户登录时校验验证码,核心代码在 LoginController.java

java

/**
 * 登录接口验证码校验
 */
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
    AjaxResult ajax = AjaxResult.success();
    // 1. 获取前端传入的UUID和验证码
    String uuid = loginBody.getUuid();
    String code = loginBody.getCode();
    
    // 2. 从Redis获取正确的验证码
    String captchaCode = redisCache.getCacheObject("captcha_codes:" + uuid);
    // 3. 校验验证码(忽略大小写)
    if (captchaCode == null || !captchaCode.equalsIgnoreCase(code)) {
        return AjaxResult.error("验证码错误");
    }
    
    // 4. 校验通过,删除Redis中的验证码(避免复用)
    redisCache.deleteObject("captcha_codes:" + uuid);
    
    // 5. 执行后续登录逻辑...
    return ajax;
}

image.gif

三、关键配置与注意事项

1. 核心配置(无需额外配置,若依默认支持)

  • Redis 配置:验证码依赖 Redis 存储,需确保 Redis 服务正常,配置正确(参考之前 Redis 配置章节);
  • 跨域配置:若前端与后端跨域,需在 WebMvcConfig 中放行 /captchaImage 接口;
  • 接口权限:/captchaImage 接口需加入 Shiro/Spring Security 白名单,无需登录即可访问: java
// ShiroConfig.java 中添加
filterChainDefinitionMap.put("/captchaImage", "anon");
  • image.gif

2. 安全性注意事项

  • 字符集:若依已排除易混淆字符(0/O、1/I),避免用户识别错误;
  • 过期时间:默认 2 分钟,不可过长(易被破解),也不可过短(用户来不及输入);
  • 防刷新:可添加接口限流(参考 Redis 限流章节),限制 /captchaImage 接口的请求频率,避免恶意刷取验证码;
  • 大小写:校验时忽略大小写(equalsIgnoreCase),提升用户体验。

四、定制化改造(按需调整)

1. 修改验证码样式

  • 调整尺寸:修改 CaptchaUtilWIDTH(宽度)、HEIGHT(高度);
  • 字符个数:修改 CODE_COUNT(如改为 6 位);
  • 干扰强度:调整 LINE_COUNT(干扰线数量)、噪点数量;
  • 字体 / 颜色:修改字体样式、字符颜色、背景色;
  • 示例:改为 6 位数字验证码(仅保留数字字符): java
// 替换字符集
private static final char[] CODE_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
// 字符个数改为6
private static final int CODE_COUNT = 6;
  • image.gif

2. 切换验证码类型(如算术验证码、滑块验证码)

若依默认是字符验证码,可扩展为算术验证码(适合低安全场景):

java

// 算术验证码生成逻辑(替换CaptchaUtil.generate()中的字符生成部分)
String num1 = String.valueOf(random.nextInt(100));
String num2 = String.valueOf(random.nextInt(100));
String operator = "+"; // 可随机+、-、×
int result = Integer.parseInt(num1) + Integer.parseInt(num2);
String code = num1 + operator + num2 + "=?"; // 验证码显示:99+88=?
// 存储到Redis的是结果:187
redisCache.setCacheObject("captcha_codes:" + uuid, String.valueOf(result), 2, TimeUnit.MINUTES);

image.gif

3. 对接第三方验证码(如极验、阿里云验证码)

若需更高安全级别的验证码(滑块、点选),可替换原生实现:

  1. 引入第三方验证码 SDK(如极验:geetest-sdk-java);
  2. 修改 CaptchaController,调用第三方接口生成验证码参数;
  3. 登录接口中调用第三方校验接口,替换 Redis 校验逻辑。

五、常见问题及解决方法

1. 验证码图片无法显示

  • 原因:图片 Base64 编码错误、跨域拦截、Redis 未启动;
  • 解决:
  1. 检查 Redis 服务是否正常,验证码 UUID 是否存入 Redis;
  2. 检查前端 <img> 标签的 src 是否正确(格式:data:image/jpeg;base64,${imgBase64});
  3. 确认 /captchaImage 接口已加入跨域 / 权限白名单。

2. 验证码校验一直失败

  • 原因:大小写未忽略、Redis 中验证码已过期、UUID 传递错误;
  • 解决:
  1. 校验时使用 equalsIgnoreCase 忽略大小写;
  2. 检查 Redis 验证码过期时间,确保用户输入时未过期;
  3. 前端确认 UUID 和验证码是否正确传递到后端。

3. 验证码生成性能低

  • 原因:图形绘制耗时、无缓存;
  • 解决:
  1. 减少干扰线 / 噪点数量(降低绘制耗时);
  2. 对验证码图片进行缓存(非核心验证码场景);
  3. 异步生成验证码(高并发场景)。

总结

若依框架图片验证码的核心实现逻辑:

  1. 生成:通过 Java 图形 API 绘制带干扰的字符图片,生成随机字符;
  2. 存储:字符存入 Redis(UUID 为 key,设置短过期时间),图片转 Base64 返回前端;
  3. 校验:登录时通过 UUID 从 Redis 读取字符,与用户输入对比,校验后删除;
  4. 定制:可调整样式、切换验证码类型,或对接第三方高安全验证码。

核心优化点:生产环境需添加接口限流、防刷新机制,高并发场景可考虑异步生成或对接第三方验证码服务。

若依最新前后端分离源码下载:

https://gitee.com/ruoyieleadmin/ruoyi-ele-admin

相关文章
|
2天前
|
人工智能 自然语言处理 JavaScript
2026年Windows+Ollama本地部署OpenClaw保姆级教程:本地AI Agent+阿里云上快速搭建
2026年OpenClaw凭借本地部署、私有化运行的特性,成为打造个人智能体的核心工具,而Ollama作为轻量级本地大模型管理工具,能让OpenClaw摆脱对云端大模型的依赖,实现**本地推理、数据不泄露、全流程私有化**的智能体验。本文基于Windows 11系统,从硬件环境准备、Ollama安装与模型定制、OpenClaw部署配置、技能扩展到常见问题排查,打造保姆级本地部署教程,同时补充阿里云OpenClaw(Clawdbot)快速部署步骤,兼顾本地私有化需求与云端7×24小时运行需求,文中所有代码命令均可直接复制执行,确保零基础用户也能快速搭建属于自己的本地智能体。
4608 20
|
9天前
|
存储 人工智能 负载均衡
阿里云OpenClaw多Agent实战宝典:从极速部署到AI团队搭建,一个人=一支高效军团
在AI自动化时代,单一Agent的“全能模式”早已无法满足复杂任务需求——记忆臃肿导致响应迟缓、上下文污染引发逻辑冲突、无关信息加载造成Token浪费,这些痛点让OpenClaw的潜力大打折扣。而多Agent架构的出现,彻底改变了这一现状:通过“单Gateway+多分身”模式,让一个Bot在不同场景下切换独立“大脑”,如同组建一支分工明确的AI团队,实现创意、写作、编码、数据分析等任务的高效协同。
3818 27
|
14天前
|
人工智能 自然语言处理 监控
OpenClaw skills重构量化交易逻辑:部署+AI全自动炒股指南(2026终极版)
2026年,AI Agent领域最震撼的突破来自OpenClaw(原Clawdbot)——这个能自主规划、执行任务的智能体,用50美元启动资金创造了48小时滚雪球至2980美元的奇迹,收益率高达5860%。其核心逻辑堪称教科书级:每10分钟扫描Polymarket近千个预测市场,借助Claude API深度推理,交叉验证NOAA天气数据、体育伤病报告、加密货币链上情绪等多维度信息,捕捉8%以上的定价偏差,再通过凯利准则将单仓位严格控制在总资金6%以内,实现低风险高频套利。
7383 66
|
3天前
|
人工智能 JSON JavaScript
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
手把手教你用 OpenClaw(v2026.2.22-2)+ 飞书,10分钟零代码搭建专属AI机器人!内置飞书插件,无需额外安装;支持Claude等主流模型,命令行一键配置。告别复杂开发,像聊同事一样自然对话。
1766 5
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
|
3天前
|
人工智能 运维 安全
OpenClaw极速部署:ZeroNews 远程管理OpenClaw Gateway Dashboard指南+常见错误解决
OpenClaw作为高性能AI智能体网关平台,其Gateway Dashboard是管理模型调用、渠道集成、技能插件的核心操作界面,但默认仅支持本地局域网访问。官方推荐的Tailscale、VPN等远程访问方案在国内网络环境中体验不佳,而ZeroNews凭借轻量化部署、专属域名映射、多重安全防护的特性,成为适配国内网络的最优远程管理解决方案。
1323 2
|
4天前
|
存储 人工智能 BI
2026年OpenClaw(Clawdbot)极简部署:接入小红书全自动运营,一个人=一支团队
2026年的小红书运营赛道,AI自动化工具已成为核心竞争力。OpenClaw(原Clawdbot)凭借“Skill插件化集成、全流程自动化、跨平台联动”的核心优势,彻底颠覆传统运营模式——从热点追踪、文案创作、封面设计到自动发布、账号互动,仅需一句自然语言指令,即可实现全链路闭环。而阿里云作为OpenClaw官方推荐的云端部署载体,2026年推出专属秒级部署方案,预装全套运行环境与小红书运营插件,让零基础用户也能10分钟完成部署,轻松拥有7×24小时在线的“专属运营团队”。
1450 6
|
8天前
|
人工智能 自然语言处理 安全
2026年OpenClaw Skills安装指南:Top20必装清单+阿里云上部署实操(附代码命令)
OpenClaw(原Clawdbot)的强大之处,不仅在于其开源免费的AI执行引擎核心,更在于其庞大的Skills生态——截至2026年2月,官方技能市场ClawHub已收录1700+各类技能插件,覆盖办公自动化、智能交互、生活服务等全场景。但对新手而言,面对海量技能往往无从下手,盲目安装不仅导致功能冗余,还可能引发权限冲突与安全风险。
2141 9
|
1月前
|
人工智能 自然语言处理 Shell
🦞 如何在 OpenClaw (Clawdbot/Moltbot) 配置阿里云百炼 API
本教程指导用户在开源AI助手Clawdbot中集成阿里云百炼API,涵盖安装Clawdbot、获取百炼API Key、配置环境变量与模型参数、验证调用等完整流程,支持Qwen3-max thinking (Qwen3-Max-2026-01-23)/Qwen - Plus等主流模型,助力本地化智能自动化。
46629 160
🦞 如何在 OpenClaw (Clawdbot/Moltbot) 配置阿里云百炼 API
|
2天前
|
人工智能 自然语言处理 安全
OpenClaw双模式部署指南:Windows+Ollama本地私有化+阿里云OpenClaw云端搭建(保姆级教程)
在AI智能体爆发的2026年,OpenClaw凭借本地部署、私有化运行、多工具集成的核心优势,成为个人与企业打造专属智能助手的首选。而Ollama作为轻量级本地大模型管理工具,能让OpenClaw彻底摆脱对云端大模型的依赖,实现“本地推理、数据不泄露、全流程私有化”的安全体验;同时阿里云提供的专属云端部署方案,可满足7×24小时稳定运行需求,兼顾隐私与便捷性。
828 2

热门文章

最新文章