跳至主要內容

若依源码解读:图片验证码

fanfan...大约 10 分钟源码解读ruoyi源码解读ruoyi

若依源码解读:图片验证码

若依通过合理的验证码生成流程和相应的代码实现,为应用程序提供了生成图片验证码和基于数学运算的验证码文本的功能,以增加系统的安全性和防护能力。 本文讨论了若依(Ruoyi)生成图片验证码的过程以及相关代码。首先,我们了解了生成图片验证码的基本步骤,包括生成随机字符串、创建图片对象、绘制背景和文本、添加干扰线等。 接下来,我们分析了若依中的验证码生成控制器,它根据配置的验证码类型,在后台生成对应类型的验证码,并将验证码图片以Base64编码的形式返回给前端页面。 最后,我们解释了一个自定义的验证码文本生成器,它生成基于数学运算的验证码文本,要求用户进行计算并输入结果进行验证。这种验证码形式提高了安全性和防止自动化攻击的能力。

1. 生成图片验证码的过程

生成图片验证码的过程包括生成随机字符串、创建图片对象、绘制背景和文本、添加干扰线、图像处理和输出验证码。这样生成的验证码图片可以用于用户身份验证、防止机器人攻击等安全性相关的场景。具体步骤如下:

  1. 生成随机字符串:首先,使用随机算法生成一串指定长度的随机字符串,通常是包含数字和字母的组合。该字符串将成为验证码的文本内容。
  2. 创建验证码图片:使用Java的图形处理库,如Java 2D或JavaFX,创建一个空白图片。一般情况下,图片的尺寸是固定的,如宽度为100像素,高度为40像素。
  3. 绘制背景:可以选择在验证码图片上绘制一些干扰背景,以增加验证码的安全性。例如,可以绘制一些随机的干扰线、噪点或颜色块。
  4. 绘制文本:使用字体库从系统中选择一个字体,将生成的随机字符串绘制到验证码图片上。为了增加验证码的可读性,通常会随机选择一种字体、字号和颜色,并将每个字符绘制在图片上不同的位置。
  5. 添加干扰线:为了增加验证码的复杂度和防止自动识别,可以在图片上绘制一些干扰线。这些干扰线可以是随机位置、随机颜色和随机形状的线条。
  6. 图片处理:对生成的验证码图片进行一些图像处理操作,例如模糊、扭曲或旋转等,以增加验证码的复杂度和安全性。
  7. 输出验证码:最后,将生成的验证码图片以某种方式输出给用户,通常是通过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;
    }
}

逐步解释这段代码的主要部分:

  1. 控制器注解:@RestController 注解表示这是一个RESTful风格的控制器,用于处理HTTP请求并返回JSON格式的响应。
  2. 注入依赖:@Resource@Autowired 注解用于依赖注入。captchaProducercaptchaProducerMath 是生成验证码的 Producer 对象,redisCache 是 Redis 缓存对象,configService 是系统配置服务。
  3. 属性注入:@Value 注解用于从配置文件中读取属性值。captchaType 是验证码的类型,该值通过 ruoyi.captchaType 属性指定。
  4. getCode 方法:@GetMapping("/captchaImage") 注解表示处理GET请求的 /captchaImage 路径。该方法用于生成验证码并返回给前端页面。
  5. 验证码开关判断:首先,通过 configService.selectCaptchaOnOff() 方法判断验证码的开关状态,将结果存入 captchaOnOff 变量,并将其放入 AjaxResult 对象中返回给前端。
  6. 生成验证码:根据 captchaType 的值选择不同类型的验证码生成方式。如果 captchaType 是 "math",则使用 captchaProducerMath 生成基于数学运算的验证码;如果 captchaType 是 "char",则使用 captchaProducer 生成基于字符的验证码。同时,记录生成的验证码字符串 capStr 和验证码的实际值 code,并生成验证码图片 image
  7. 保存验证码信息:生成一个唯一的 uuid 作为验证码的标识符,将 code 存入 Redis 缓存中,并设置过期时间为 Constants.CAPTCHA_EXPIRATION 分钟。
  8. 图片转换和返回:将生成的验证码图片转换为字节数组,使用 Base64.encode 方法将字节数组转换为字符串,并将 uuid 和图片字符串放入 AjaxResult 对象中返回给前端。

2. 验证码文本生成器: 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();
    }
}

让我逐步解释这段代码的主要部分:

  1. 类定义和继承:KaptchaTextCreator 类继承自 DefaultTextCreator,后者是 kaptcha 库中提供的默认文本生成器。
  2. 静态常量定义:CNUMBERS 是一个包含数字0到10的字符串数组。
  3. getText 方法重写:重写了父类的 getText 方法,用于生成验证码的文本内容。
  4. 生成验证码文本:首先,通过 Random 类生成两个随机数 xy,范围为0到9。然后,根据随机操作数的值 randomoperands,进行不同的数学运算。
    • 如果 randomoperands 是0,表示进行乘法运算。将 xy 相乘,将运算表达式和结果添加到 suChinese 字符串中。
    • 如果 randomoperands 是1,表示进行除法或加法运算。如果 x 不为0,并且 y 能整除 x,则进行除法运算,将结果添加到 suChinese 中;否则进行加法运算,将结果添加到 suChinese 中。
    • 如果 randomoperands 是2,表示进行减法运算。如果 x 大于等于 y,进行 x - y 运算,否则进行 y - x 运算,将结果添加到 suChinese 中。
    • 如果 randomoperands 不是上述情况,表示进行加法运算,将 xy 相加,将运算表达式和结果添加到 suChinese 中。
  5. 返回验证码文本:最后,将运算表达式和结果连接为一个字符串,并添加特殊字符 "=?@",最终返回生成的验证码文本。

3.注册到 Spring 容器中

若你想用在项目,需要去配置验证码生成器,别忘了在合适的位置加上以下代码,并根据实际情况去调整。

以下代码CaptchaConfig,用于配置验证码生成器——Google的Kaptcha库。这个配置类通过两个@Bean方法定义了两种不同风格的验证码生成器实例,并将它们分别注入到Spring容器中,以便在应用程序中使用。

  1. getKaptchaBean() 方法:
    • 创建一个 DefaultKaptcha 实例,这是 Kaptcha 库默认的验证码生成器。
    • 定义一系列属性(以 KAPTCHA_ 开头),这些属性用来配置验证码的样式和行为,如边框、文本颜色、大小、长度等。
    • 将所有属性封装到 Properties 对象中。
    • 使用 Config 类将 Properties 对象转换为 Kaptcha 配置对象。
    • 设置 DefaultKaptcha 的配置,使其按照给定的属性生成验证码图片。
    • 最后,返回配置好的 DefaultKaptcha 实例,并将其注册到 Spring 容器中,bean 名称为 "captchaProducer"。
  2. getKaptchaBeanMath() 方法:
    • 与上一个方法类似,创建并配置另一个 DefaultKaptcha 实例,但这次有一些不同的属性设置,例如文本颜色、字符间距、字符长度以及验证码类型(这里通过自定义的 KaptchaTextCreator 文本生成器实现数学算式验证码)。
    • 其他配置项包括噪音效果、背景色等,也进行了自定义设置。
    • 同样地,将配置好的 DefaultKaptcha 注入到 Spring 容器中,bean 名称为 "captchaProducerMath"。

代码提供了两种不同类型的验证码生成器配置,分别用于生成普通字符验证码和数学算式验证码,在Spring应用启动时会加载这些配置,并根据需要注入到相关服务或组件中。

package com.ruoyi.framework.config;

import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;

/**
 * 验证码配置
 * 
 * @author ruoyi
 */
@Configuration
public class CaptchaConfig
{
    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean()
    {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框 默认为true 我们可以自己设置yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 验证码文本字符颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
        // 验证码图片宽度 默认为200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 验证码图片高度 默认为50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小 默认为40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
        // 验证码文本字符长度 默认为5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    @Bean(name = "captchaProducerMath")
    public DefaultKaptcha getKaptchaBeanMath()
    {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框 默认为true 我们可以自己设置yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 边框颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
        // 验证码文本字符颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        // 验证码图片宽度 默认为200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 验证码图片高度 默认为50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小 默认为40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
        // 验证码文本生成器
        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator");
        // 验证码文本字符间距 默认为2
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
        // 验证码文本字符长度 默认为5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 验证码噪点颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
        // 干扰实现类
        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}
上次编辑于:
贡献者: fanfan
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8
Off