若依源码解析:图片验证码生成
摘要
若依通过合理的验证码生成流程和相应的代码实现,为应用程序提供了生成图片验证码和基于数学运算的验证码文本的功能,以增加系统的安全性和防护能力。
本文讨论了若依(Ruoyi)生成图片验证码的过程以及相关代码。首先,我们了解了生成图片验证码的基本步骤,包括生成随机字符串、创建图片对象、绘制背景和文本、添加干扰线等。
接下来,我们分析了若依中的验证码生成控制器,它根据配置的验证码类型,在后台生成对应类型的验证码,并将验证码图片以Base64编码的形式返回给前端页面。
最后,我们解释了一个自定义的验证码文本生成器,它生成基于数学运算的验证码文本,要求用户进行计算并输入结果进行验证。这种验证码形式提高了安全性和防止自动化攻击的能力。
生成图片验证码的过程
生成图片验证码的过程包括生成随机字符串、创建图片对象、绘制背景和文本、添加干扰线、图像处理和输出验证码。这样生成的验证码图片可以用于用户身份验证、防止机器人攻击等安全性相关的场景。具体步骤如下:
生成随机字符串:首先,使用随机算法生成一串指定长度的随机字符串,通常是包含数字和字母的组合。该字符串将成为验证码的文本内容。
创建验证码图片:使用Java的图形处理库,如Java 2D或JavaFX,创建一个空白图片。一般情况下,图片的尺寸是固定的,如宽度为100像素,高度为40像素。
绘制背景:可以选择在验证码图片上绘制一些干扰背景,以增加验证码的安全性。例如,可以绘制一些随机的干扰线、噪点或颜色块。
绘制文本:使用字体库从系统中选择一个字体,将生成的随机字符串绘制到验证码图片上。为了增加验证码的可读性,通常会随机选择一种字体、字号和颜色,并将每个字符绘制在图片上不同的位置。
添加干扰线:为了增加验证码的复杂度和防止自动识别,可以在图片上绘制一些干扰线。这些干扰线可以是随机位置、随机颜色和随机形状的线条。
图片处理:对生成的验证码图片进行一些图像处理操作,例如模糊、扭曲或旋转等,以增加验证码的复杂度和安全性。
输出验证码:最后,将生成的验证码图片以某种方式输出给用户,通常是通过HTTP响应返回给前端页面。前端页面可以将该图片显示给用户,并且用户需要输入验证码的文本内容进行验证。
若依中的验证码生成控制器:CaptchaController
若依(Ruoyi)通过CaptchaController来生成验证码并返回给前端页面。根据配置的验证码类型,在后台生成对应类型的验证码,并将验证码的实际值存入缓存中,然后将验证码图片以Base64编码的形式返回给前端页面。
@RestController
public class CaptchaController
{
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private RedisCache redisCache;
// 验证码类型
@Value("${ruoyi.captchaType}")
private String captchaType;
@Autowired
private ISysConfigService configService;
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException
{
AjaxResult ajax = AjaxResult.success();
boolean captchaOnOff = configService.selectCaptchaOnOff();
ajax.put("captchaOnOff", captchaOnOff);
if (!captchaOnOff)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
}
else if ("char".equals(captchaType))
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
}
逐步解释这段代码的主要部分:
控制器注解:
@RestController
注解表示这是一个RESTful风格的控制器,用于处理HTTP请求并返回JSON格式的响应。注入依赖:
@Resource
和@Autowired
注解用于依赖注入。captchaProducer
和captchaProducerMath
是生成验证码的Producer
对象,redisCache
是 Redis 缓存对象,configService
是系统配置服务。属性注入:
@Value
注解用于从配置文件中读取属性值。captchaType
是验证码的类型,该值通过ruoyi.captchaType
属性指定。getCode
方法:@GetMapping("/captchaImage")
注解表示处理GET请求的/captchaImage
路径。该方法用于生成验证码并返回给前端页面。验证码开关判断:首先,通过
configService.selectCaptchaOnOff()
方法判断验证码的开关状态,将结果存入captchaOnOff
变量,并将其放入AjaxResult
对象中返回给前端。生成验证码:根据
captchaType
的值选择不同类型的验证码生成方式。如果captchaType
是 "math",则使用captchaProducerMath
生成基于数学运算的验证码;如果captchaType
是 "char",则使用captchaProducer
生成基于字符的验证码。同时,记录生成的验证码字符串capStr
和验证码的实际值code
,并生成验证码图片image
。保存验证码信息:生成一个唯一的
uuid
作为验证码的标识符,将code
存入 Redis 缓存中,并设置过期时间为Constants.CAPTCHA_EXPIRATION
分钟。图片转换和返回:将生成的验证码图片转换为字节数组,使用
Base64.encode
方法将字节数组转换为字符串,并将uuid
和图片字符串放入AjaxResult
对象中返回给前端。
验证码文本生成器:KaptchaTextCreator
这段代码是一个验证码文本生成器,用于生成基于数学运算的验证码文本。该验证码文本生成器在生成验证码时,采用基于数学运算的方式,使得用户需要计算验证码的结果,并输入到验证码输入框中进行验证。这种验证码形式增加了安全性和防止自动化攻击的能力。
/**
* 验证码文本生成器
*
* @author ruoyi
*/
public class KaptchaTextCreator extends DefaultTextCreator
{
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
@Override
public String getText()
{
Integer result = 0;
Random random = new Random();
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
int randomoperands = (int) Math.round(Math.random() * 2);
if (randomoperands == 0)
{
result = x * y;
suChinese.append(CNUMBERS[x]);
suChinese.append("*");
suChinese.append(CNUMBERS[y]);
}
else if (randomoperands == 1)
{
if (!(x == 0) && y % x == 0)
{
result = y / x;
suChinese.append(CNUMBERS[y]);
suChinese.append("/");
suChinese.append(CNUMBERS[x]);
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
}
else if (randomoperands == 2)
{
if (x >= y)
{
result = x - y;
suChinese.append(CNUMBERS[x]);
suChinese.append("-");
suChinese.append(CNUMBERS[y]);
}
else
{
result = y - x;
suChinese.append(CNUMBERS[y]);
suChinese.append("-");
suChinese.append(CNUMBERS[x]);
}
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
suChinese.append("=?@" + result);
return suChinese.toString();
}
}
让我逐步解释这段代码的主要部分:
类定义和继承:
KaptchaTextCreator
类继承自DefaultTextCreator
,后者是kaptcha
库中提供的默认文本生成器。静态常量定义:
CNUMBERS
是一个包含数字0到10的字符串数组。getText
方法重写:重写了父类的getText
方法,用于生成验证码的文本内容。生成验证码文本:首先,通过
Random
类生成两个随机数x
和y
,范围为0到9。然后,根据随机操作数的值randomoperands
,进行不同的数学运算。- 如果
randomoperands
是0,表示进行乘法运算。将x
和y
相乘,将运算表达式和结果添加到suChinese
字符串中。 - 如果
randomoperands
是1,表示进行除法或加法运算。如果x
不为0,并且y
能整除x
,则进行除法运算,将结果添加到suChinese
中;否则进行加法运算,将结果添加到suChinese
中。 - 如果
randomoperands
是2,表示进行减法运算。如果x
大于等于y
,进行x - y
运算,否则进行y - x
运算,将结果添加到suChinese
中。 - 如果
randomoperands
不是上述情况,表示进行加法运算,将x
和y
相加,将运算表达式和结果添加到suChinese
中。
- 如果
返回验证码文本:最后,将运算表达式和结果连接为一个字符串,并添加特殊字符 "=?@",最终返回生成的验证码文本。