图形验证码的实现

图形验证码的实现

使用技术:SpringBoot+redis

  1. 导入第三方依赖

       <!-- 验证码 -->
    <dependency>
        <groupId>pro.fessional</groupId>
        <artifactId>kaptcha</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>servlet-api</artifactId>
                <groupId>javax.servlet</groupId>
            </exclusion>
        </exclusions>
        <version>2.3.3</version>
    </dependency>
    
  2. 新建配置类 CaptchaConfig 对验证码图片进行设置,例如

    package com.fs.system.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.*;
    
    /**
     * 验证码配置
     *
     */
    @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.fs.system.util.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;
        }
    }
    
    

    主要:这里实现了两种验证码:

    • 第一种: 是随机字符串的验证码, 我们只需要注入名为captchaProducer的bean
    • 第二种: 是数学运算的验证码, 我们只需要注入名为captchaProducerMath的bean

​ 在第二种方式中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文本生成器为自定义的工具类,具体实现如下

package com.fs.system.util;

import com.google.code.kaptcha.text.impl.DefaultTextCreator;

import java.util.Random;

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 = random.nextInt(3);
        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 (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]);
            }
        }
        suChinese.append("=?@" + result);
        return suChinese.toString();  //5+6=?@11
    }
}

在工具类中,限制了运算数字为0-9,并且对结果做了规范化要求。

  1. 编写Controller接口

    具体流程:

    1. 判断是否开启验证码,如果没有就直接返回

    2. 如果有开启验证码,获取验证码的类型(math,char)

    3. 如果是math类型, 使用bean名字为captchaProducerMath来产生验证码

    4. 如果是char类型, 使用bean名字为captchaProducer来产生验证码

    5. 把生成的验证码存储在redis中, redis的key: CacheConstants.CAPTCHA_CODE_KEY + uuid

    6. 使用ImageIO,把生成验证码图片变成字节数组

    7. 使用Base64对验证码字节数组进行编码, 封装到AjaxResult中

    8. 响应AjaxResult的json给前端

​ 具体实现代码:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.lang.UUID;
import com.fs.common.config.RuoYiConfig;
import com.fs.common.constant.CacheConstants;
import com.fs.common.constant.Constants;
import com.fs.common.core.vo.AjaxResult;
import com.fs.common.util.RedisCache;
import com.fs.system.service.ISysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import sun.misc.BASE64Encoder;

/**
 * 验证码操作处理
 * 
 */
@RestController
public class CaptchaController
{
    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @Resource(name = "captchaProducerMath")
    private Producer captchaProducerMath;

    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private ISysConfigService configService;
    /**
     * 生成验证码
     */
    @GetMapping("/captchaImage")
    public AjaxResult getCode(HttpServletResponse response) throws IOException
    {
        AjaxResult ajax = AjaxResult.success();
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        ajax.put("captchaEnabled", captchaEnabled);
        if (!captchaEnabled)
        {
            return ajax;
        }

        // 保存验证码信息
        String uuid = UUID.randomUUID().toString(true);
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;

        String capStr = null, code = null;
        BufferedImage image = null;

       // 生成验证码
        String captchaType = FeiSiConfig.getCaptchaType();
        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);
        BASE64Encoder encoder = new BASE64Encoder();
        String str = "data:image/jpeg;base64,";
        //把图片进行base64编码
        String base64Img = str + encoder.encode(os.toByteArray());
        //BASE64对密文进行传输加密时,可能出现\r\n
        //原因: RFC2045中有规定:即Base64一行不能超过76字符,超过则添加回车换行符。
        //解决方案: BASE64加密后,对\r\n进行去除
        base64Img= base64Img.replaceAll("\r|\n", "");
        ajax.put("img", base64Img);
        return ajax;
    }
}

同时,具体的参数配置在一个全局配置类中进行封装,下面是验证码相关

package com.fs.common.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 读取项目相关配置
 *
 */
@Component
@ConfigurationProperties(prefix = "my)
public class MyConfig
{
   
    /** 验证码类型 */
    private static String captchaType;

    public static String getCaptchaType() {
        return captchaType;
    }

    public void setCaptchaType(String captchaType) {
        FeiSiConfig.captchaType = captchaType;
    }

}

在application.yml的相关配置:

# 系统的全局配置
# 项目相关配置
my:
  # 验证码类型 math 数字计算 char 字符验证
  captchaType: math

总结

​ 实现验证码第一步就是要找好需要实现的验证码的第三方工具,然后导入对应依赖。第二步,根据使用的工具进行个性化配置。最后才是具体去使用的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值