Java编码技巧:验证码

目录

    • 1.1、EasyCaptcha(优选,支持种类多,样式多,使用简单)
      • 1.1.1、作用
      • 1.1.2、官方信息
      • 1.1.3、使用案例
      • 1.1.4、依赖
      • 1.1.5、代码
      • 1.1.6、效果
      • 1.1.7、拓展
    • 1.2、kaptcha
      • 1.2.1、作用
      • 1.2.2、官方信息
      • 1.2.3、使用案例
      • 1.2.4、依赖
      • 1.2.5、代码
      • 1.2.6、效果
    • 1.3、AJ-Captcha(TODO)
      • 1.3.1、作用
      • 1.3.2、官方信息
      • 1.3.3、依赖
      • 1.3.4、代码
      • 1.3.5、效果
    • 1.4、tianai-captcha(TODO)
      • 1.4.1、作用
      • 1.4.2、官方信息
      • 1.4.3、依赖
      • 1.4.4、代码
      • 1.4.5、效果
    • 1.5、hutool验证码
      • 1.5.1、作用
      • 1.5.2、官方信息
      • 1.5.3、使用案例
      • 1.5.4、依赖
      • 1.5.5、代码
      • 1.5.6、效果
      • 1.5.7、拓展
    • 1.6、实战
      • 1.6.1、使用场景
      • 1.6.2、用途讲解
        • 1.6.2.1、流程串讲
        • 1.6.2.2、后端验证码校验—网关层校验
        • 1.6.2.3、后端验证码校验—直接校验

1.1、EasyCaptcha(优选,支持种类多,样式多,使用简单)

1.1.1、作用

用作Java图形验证码,可用于Java Web、JavaSE等项目,支持情况如下:

  • 支持 数字 / 字母 png、数字 / 字母 gif、中文png、中文gif、算术png类型
  • 支持设置字段长度、算数数字长度;当然每一种类型都有默认值
  • 支持设置字体;当然也有默认字体
  • 支持为数字 / 字母 类验证码设置“验证码文本类型”,包含类型有:字母数字混合(默认值)、纯数字、纯字母、纯大写字母、纯小写字母、数字大写字母

效果演示:
在这里插入图片描述

1.1.2、官方信息

  • gitee项目代码

1.1.3、使用案例

  • renren-security

1.1.4、依赖

<dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version>
</dependency>

1.1.5、代码

import com.wf.captcha.*;public class Test {public static void main(String[] args) {/** 说明:下面一共展示了5种样式的详细使用方法,它们分别是:* 1、字母/数字 png类型* 2、字母/数字 gif类型* 3、中文 png类型* 4、中文 gif类型* 5、算术 png类型*/// 1、字母/数字 png类型(支持样式:字母数字混合(默认值)、纯数字、纯字母、纯大写字母、纯小写字母、数字大写字母)SpecCaptcha captcha = new SpecCaptcha();// 设置验证码显示宽度;宽度默认值:130
//        captcha.setWidth(150);// 设置验证码显示高度;高度默认值:48
//        captcha.setHeight(40);// 设置验证码随机字符长度;默认值:5;比如:asdfg就是5个字符
//        captcha.setLen(4);// 设置验证码文本类型;默认值:TYPE_DEFAULT(字母数字混合)
//        captcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);// 设置验证码的字体;默认值:actionj字体、加粗Font.BOLD、字体大小32磅
//        captcha.setFont(new Font("Verdana", Font.PLAIN, 32));// 验证码结果System.out.println(">>>>>>>>>>>>>>>>>>>> 字母/数字 png类型 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha.text());System.out.println("验证码Base64编码:" + captcha.toBase64());System.out.println(">>>>>>>>>>>>>>>>>>>> 字母/数字 png类型 end <<<<<<<<<<<<<<<<<<<<");// 2、字母/数字 gif类型(支持样式:字母数字混合(默认值)、纯数字、纯字母、纯大写字母、纯小写字母、数字大写字母)GifCaptcha captcha2 = new GifCaptcha();// 设置验证码显示宽度;宽度默认值:130
//        captcha2.setWidth(150);// 设置验证码显示高度;高度默认值:48
//        captcha2.setHeight(40);// 设置验证码随机字符长度;默认值:5;比如:asdfg就是5个字符
//        captcha2.setLen(4);// 设置验证码文本类型;默认值:TYPE_DEFAULT(字母数字混合)
//        captcha2.setCharType(Captcha.TYPE_NUM_AND_UPPER);// 设置验证码的字体;默认值:actionj字体、Font.BOLD(加粗)、32磅
//        captcha2.setFont(new Font("Verdana", Font.PLAIN, 32));// 验证码结果System.out.println("\n\n\n>>>>>>>>>>>>>>>>>>>> 字母/数字 gif类型 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha2.text());System.out.println("验证码Base64编码:" + captcha2.toBase64());System.out.println(">>>>>>>>>>>>>>>>>>>> 字母/数字 gif类型 end <<<<<<<<<<<<<<<<<<<<");// 3、中文 png类型ChineseCaptcha captcha3 = new ChineseCaptcha();// 设置验证码显示宽度;宽度默认值:130
//        captcha3.setWidth(150);// 设置验证码显示高度;高度默认值:48
//        captcha3.setHeight(40);// 设置验证码随机字符长度;默认值:4,详情可看ChineseCaptchaAbstract的无参构造函数;比如:高山流水就是4个字符
//        captcha3.setLen(4);// 设置验证码的字体;默认值:楷体、 Font.PLAIN(不加粗)、 28磅
//        captcha3.setFont(new Font("黑体", Font.PLAIN, 32));// 验证码结果System.out.println("\n\n\n>>>>>>>>>>>>>>>>>>>> 中文 png类型 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha3.text());System.out.println("验证码Base64编码:" + captcha3.toBase64());System.out.println(">>>>>>>>>>>>>>>>>>>> 中文 png类型 end <<<<<<<<<<<<<<<<<<<<");// 4、中文 gif类型ChineseGifCaptcha captcha4 = new ChineseGifCaptcha();// 设置验证码显示宽度;宽度默认值:130
//        captcha4.setWidth(150);// 设置验证码显示高度;高度默认值:48
//        captcha4.setHeight(40);// 设置验证码随机字符长度;默认值:4,详情可看ChineseCaptchaAbstract的无参构造函数;比如:高山流水就是4个字符
//        captcha4.setLen(4);// 设置验证码的字体;默认值:楷体、Font.PLAIN(不加粗)、28磅,详情可看ChineseCaptchaAbstract的无参构造函数
//        captcha4.setFont(new Font("黑体", Font.PLAIN, 32));// 验证码结果System.out.println("\n\n\n>>>>>>>>>>>>>>>>>>>> 中文 gif类型 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha4.text());System.out.println("验证码Base64编码:" + captcha4.toBase64());System.out.println(">>>>>>>>>>>>>>>>>>>> 中文 gif类型 end <<<<<<<<<<<<<<<<<<<<");// 5、算术 png类型ArithmeticCaptcha captcha5 = new ArithmeticCaptcha(150, 40);// 设置验证码显示宽度;宽度默认值:130
//        captcha5.setWidth(150);// 设置验证码显示高度;高度默认值:48
//        captcha5.setHeight(40);// 设置验证码数字长度;默认值:2,详情可看ArithmeticCaptchaAbstract的无参构造函数;比如:“3 + 2 = ?”就是2个字符
//        captcha5.setLen(3);// 设置验证码的字体;默认值:楷体, Font.PLAIN(平常字体), 28
//        captcha5.setFont(new Font("黑体", Font.PLAIN, 32));// 验证码结果System.out.println("\n\n\n>>>>>>>>>>>>>>>>>>>> 算术 png类型 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha5.text());System.out.println("验证码内容:" + captcha5.getArithmeticString());System.out.println("验证码Base64编码:" + captcha5.toBase64());System.out.println(">>>>>>>>>>>>>>>>>>>> 算术 png类型 end <<<<<<<<<<<<<<<<<<<<");}
}

1.1.6、效果

查看验证码图片效果方式:

下面返回了Base64编码结果,如果大家想查看具体图片效果,可以根据下面html代码新建一个以html后缀结尾的文件,然后将img标签中的src属性替换成验证码Base64编码结果即可,如下:

<!DOCTYPE HTML>
<html>
<head><title>测试验证码</title><meta charset="utf-8">
</head>
<body>
<img src="验证码Base64编码"/>
</body>

代码执行结果:

>>>>>>>>>>>>>>>>>>>> 字母/数字 png类型 start <<<<<<<<<<<<<<<<<<<<
验证码结果:utGBu
验证码Base64编码:data:image/png;base64,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
>>>>>>>>>>>>>>>>>>>> 字母/数字 png类型 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 字母/数字 gif类型 start <<<<<<<<<<<<<<<<<<<<
验证码结果:Z3N6N
验证码Base64编码:data:image/gif;base64,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
>>>>>>>>>>>>>>>>>>>> 字母/数字 gif类型 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 中文 png类型 start <<<<<<<<<<<<<<<<<<<<
验证码结果:志报背发
验证码Base64编码:data:image/png;base64,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
>>>>>>>>>>>>>>>>>>>> 中文 png类型 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 中文 gif类型 start <<<<<<<<<<<<<<<<<<<<
验证码结果:清却衣是
验证码Base64编码:data:image/gif;base64,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
>>>>>>>>>>>>>>>>>>>> 中文 gif类型 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 算术 png类型 start <<<<<<<<<<<<<<<<<<<<
验证码结果:5
验证码内容:9-4=?
验证码Base64编码:data:image/png;base64,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
>>>>>>>>>>>>>>>>>>>> 算术 png类型 end <<<<<<<<<<<<<<<<<<<<

1.1.7、拓展

背景: 上面解释了如何获取验证码结果以及验证码Base64编码字符串,其实我们还可以直接把验证码结果输出到响应流中,请看下面代码

代码:

import com.wf.captcha.SpecCaptcha;@GetMapping("/previewCaptcha")
public void previewCaptcha(HttpServletResponse response) throws IOException {try (ServletOutputStream outputStream = response.getOutputStream();) {// 将验证码图片返回到前端response.setContentType("image/png");SpecCaptcha captcha = new SpecCaptcha(150, 40);captcha.setLen(4);// 将验证码图片返回captcha.out(outputStream);// 将验证码结果存储到redis中,具体过程省略String result = captcha.text();// 存储到redis中……} catch (Exception e) {e.printStackTrace();}
}

结果:

在这里插入图片描述

1.2、kaptcha

1.2.1、作用

用作Java图形验证码,可用于Java Web、JavaSE等项目,支持情况如下:

  • 支持数字、字母验证码,默认支持
  • 支持自定义验证码内容,比如:算数验证码

相比上面EasyCaptcha来说,这种方式虽然实现比较复杂,但是留给开发者的可修改空间较大,用户可以根据自己的需要去自定义验证码图片样式以及内容等

1.2.2、官方信息

大型项目技术栈第九讲 kaptcha的使用

1.2.3、使用案例

  • ruoyi-cloud

1.2.4、依赖

<dependency><groupId>pro.fessional</groupId><artifactId>kaptcha</artifactId><version>2.3.3</version>
</dependency>

1.2.5、代码

以下示例代码来自于: ruoyi-cloud项目的ruoyi-gateway模块下的config目录下的CaptchaConfigKaptchaTextCreator类,以及service目录的impl目录下的ValidateCodeServiceImpl

验证码配置类CaptchaConfig:

作用:定义两种类型的验证码,分别是:默认文本验证码、自定义算术验证码

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Properties;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,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 验证码文本字符颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");// 验证码图片宽度 默认为200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 验证码图片高度 默认为50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 验证码文本字符大小 默认为40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");// 随机生成字符的范围,默认值:abcde2345678gfynmnpwxproperties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");// 验证码文本字符长度 默认为5properties.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.ShadowGimpyproperties.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,noproperties.setProperty(KAPTCHA_BORDER, "yes");// 边框颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");// 验证码文本字符颜色 默认为Color.BLACKproperties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");// 验证码图片宽度 默认为200properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");// 验证码图片高度 默认为50properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");// 验证码文本字符大小 默认为40properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");// KAPTCHA_SESSION_KEYproperties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");// 验证码文本生成器properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator");// 验证码文本字符间距 默认为2properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");// 验证码文本字符长度 默认为5properties.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.BLACKproperties.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.ShadowGimpyproperties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}
}

算术验证码生成器类KaptchaTextCreator:

作用:为上面验证码配置类CaptchaConfiggetKaptchaBeanMath()提供文本实现类

import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;/*** 验证码文本生成器* * @author ruoyi*/
public class KaptchaTextCreator extends DefaultTextCreator
{private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");@Overridepublic 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 (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();}
}

验证码实现处理类ValidateCodeServiceImpl:

说明:

  • 针对生成验证码createCaptcha方法来说:首先通过CaptchaProperties配置类来判断是否需要生成验证码,以及生成的验证码类型,然后将创建的验证码对应uuid + Base64编码字符串返回给前端,另外一方面把验证码对应uuid+验证码结果存入redis
  • 针对校验验证码checkCaptcha方法来说:根据用户提交的uuid和验证码结果作为依托,通过uuid去redis中查询真实验证码结果,然后和用户提交的验证码结果作对比,一致说明ok,否则说明用户输入验证码结果有问题,那就需要刷新验证码图片进行重新输入

下面代码中用到的一些内容没有粘贴过来,大家可以去看ruoyi-cloud项目,ValidateCodeServiceImpl类的具体位置是:ValidateCodeServiceImpl

import com.google.code.kaptcha.Producer;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.sign.Base64;
import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.CaptchaProperties;
import com.ruoyi.gateway.service.ValidateCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;/*** 验证码实现处理** @author ruoyi*/
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService
{// 数字/字母文本验证码@Resource(name = "captchaProducer")private Producer captchaProducer;// 算术验证码@Resource(name = "captchaProducerMath")private Producer captchaProducerMath;@Autowiredprivate RedisService redisService;@Autowiredprivate CaptchaProperties captchaProperties;/*** 生成验证码*/@Overridepublic AjaxResult createCaptcha() throws IOException, CaptchaException{AjaxResult ajax = AjaxResult.success();boolean captchaEnabled = captchaProperties.getEnabled();ajax.put("captchaEnabled", captchaEnabled);if (!captchaEnabled){return ajax;}// 保存验证码信息String uuid = IdUtils.simpleUUID();String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;String capStr = null, code = null;BufferedImage image = null;String captchaType = captchaProperties.getType();// 生成验证码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);}redisService.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;}/*** 校验验证码*/@Overridepublic void checkCaptcha(String code, String uuid) throws CaptchaException{if (StringUtils.isEmpty(code)){throw new CaptchaException("验证码不能为空");}if (StringUtils.isEmpty(uuid)){throw new CaptchaException("验证码已失效");}String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;String captcha = redisService.getCacheObject(verifyKey);redisService.deleteObject(verifyKey);if (!code.equalsIgnoreCase(captcha)){throw new CaptchaException("验证码错误");}}
}

1.2.6、效果

算数验证码:

在这里插入图片描述

文本验证码:

在这里插入图片描述

1.3、AJ-Captcha(TODO)

1.3.1、作用

支持滑动拼图文字点选这两种方式验证码类型

在这里插入图片描述

1.3.2、官方信息

  • gitee代码

1.3.3、依赖

整理麻烦,以后在整理

1.3.4、代码

整理麻烦,以后在整理

1.3.5、效果

整理麻烦,以后在整理

1.4、tianai-captcha(TODO)

1.4.1、作用

支持多种验证码方式,包括:

  • 滑块验证码
  • 旋转验证码
  • 滑动还原验证码
  • 文字点选验证码

实现效果如下图:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.4.2、官方信息

  • gitee项目

1.4.3、依赖

整理麻烦,以后在整理

1.4.4、代码

整理麻烦,以后在整理

1.4.5、效果

整理麻烦,以后在整理

1.5、hutool验证码

1.5.1、作用

用作Java图形验证码,可用于Java Web、JavaSE等项目,支持情况如下:

  • 支持验证码图片的线段干扰、圆圈干扰、扭曲干扰3种干扰方式
  • 默认情况下生成数字 / 字母混合的验证码图片,支持自定义验证码内容

1.5.2、官方信息

  • hutool图形验证码文档

1.5.3、使用案例

  • Snowy-Cloud

1.5.4、依赖

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version>
</dependency>

1.5.5、代码

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;public class Test {public static void main(String[] args) {// 线段干扰验证码// 参数含义:图片宽度、图片高度、字符个数、干扰线条数LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 38, 4, 4);// 验证码结果System.out.println(">>>>>>>>>>>>>>>>>>>> 线段干扰验证码 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha.getCode());System.out.println("验证码Base64编码:" + captcha.getImageBase64Data());System.out.println(">>>>>>>>>>>>>>>>>>>> 线段干扰验证码 end <<<<<<<<<<<<<<<<<<<<\n\n\n");// 圆圈干扰验证码// 参数含义:图片宽度、图片高度、字符个数、干扰圆圈条数CircleCaptcha captcha2 = CaptchaUtil.createCircleCaptcha(100, 38, 4, 5);// 验证码结果System.out.println(">>>>>>>>>>>>>>>>>>>> 圆圈干扰验证码 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha2.getCode());System.out.println("验证码Base64编码:" + captcha2.getImageBase64Data());System.out.println(">>>>>>>>>>>>>>>>>>>> 圆圈干扰验证码 end <<<<<<<<<<<<<<<<<<<<\n\n\n");// 扭曲干扰验证码// 参数含义:图片宽度、图片高度、字符个数、干扰线宽度ShearCaptcha captcha3 = CaptchaUtil.createShearCaptcha(100, 38, 4, 5);// 验证码结果System.out.println(">>>>>>>>>>>>>>>>>>>> 扭曲干扰验证码 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha3.getCode());System.out.println("验证码Base64编码:" + captcha3.getImageBase64Data());System.out.println(">>>>>>>>>>>>>>>>>>>> 扭曲干扰验证码 end <<<<<<<<<<<<<<<<<<<<\n\n\n");// 说明:自定义验证码代码以线段干扰验证码LineCaptcha为例,讲解相关使用方法,其中LineCaptcha可以替换成另外两种验证码类型,这里不在演示// 自定义纯数字验证码(hutool官方)// 参数含义:图片宽度、图片高度、字符个数、干扰线条数LineCaptcha captcha4 = CaptchaUtil.createLineCaptcha(100, 38, 4, 4);// 自定义纯数字的验证码(随机4位数字,可重复)captcha4.setGenerator(new RandomGenerator("0123456789", 4));// 验证码结果System.out.println(">>>>>>>>>>>>>>>>>>>> 自定义纯数字验证码 start <<<<<<<<<<<<<<<<<<<<");System.out.println("验证码结果:" + captcha4.getCode());System.out.println("验证码Base64编码:" + captcha4.getImageBase64Data());System.out.println(">>>>>>>>>>>>>>>>>>>> 自定义纯数字验证码 end <<<<<<<<<<<<<<<<<<<<");}
}

1.5.6、效果

下面返回了Base64编码结果,如果大家想查看具体图片效果,可以根据下面html代码新建一个以html后缀结尾的文件,然后将img标签中的src属性替换成验证码Base64编码结果即可,如下:

<!DOCTYPE HTML>
<html>
<head><title>测试验证码</title><meta charset="utf-8">
</head>
<body>
<img src="验证码Base64编码"/>
</body>

代码执行结果:

>>>>>>>>>>>>>>>>>>>> 线段干扰验证码 start <<<<<<<<<<<<<<<<<<<<
验证码结果:4ose
验证码Base64编码:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAmCAYAAAAycj4zAAAEqklEQVR42u2Y+2tbZRjH9x/4sz/4g4g/CCJjKqNMSv1FpF5Qp5vKFOZQEFbmZWW6m1M2Joi6eUFZV6FeJjoYqFPKxAubTdNLQtI0vaVpSiwzxx6Tk560SU7O+z7meQ5J0zQ55Vzamvg+5eWQ837JS55Pn/e5bIEGtwduPE+rWWxLs/yQEphGh9M0QOrBaTRATQmkka+1/wUQESHCBBABRJgAIoBU2Pz0FfCca6ElbJOBLCVnwPtZmwDyXwCia0vg/2ZXGYYAsslAJn9+nSDEPO8LIJsN5HroWwIQ+u4FAM4EkM0EspAIQX/XDhj6oh3yizK9cwtIX+4qvJZ6Ge6VtsOOxFZ6dqY6oD9/zfF35zSAc99zeOwIg1uf0uHmXTrctkeHZ08y+OoKB8bdc95CWoWpiWkY7PeDt2+YnuPhKZDn/3EXiJZNwdCXD5LzlbnB8nunQDSuwWHlFdieuL3uOqp0gl78s2NKBuD+gwxu2qnXXe2dDDJZ5zCi07PQ/8dQ3TUxFgHGmDtAwpf3k+P/9K0cyDkF8qZymJyOEXFhsQcUlqL3KZaEzzPd9B73T6aP2fr+o10GDIyOgbHlaNAKAJc9HFpeNKC88zVzBCM+O0dOHx4MQOK6BBoeUDTOOcxLMviHR2g/MjnjHEh8uIucPt776iqxEyCjWpCcfY+0DcJaqK4G91E3roUtn4FXEzpcXaq9Pz0HtI9g7Fp2KUvO9hVhaHmtpqZQ0CHgC5FOXcjYB5KMe8jhvguPQCGruArkdPoEOfoT9aypDvdRdyr9huUzbnnSADISdTFRVNlsLE6O/luSTXWYR1AXjcTsAclnEjDYcx94u1tBlUZrip0A2SPvJEdXRse+9tWRgpGBut3yw5bP2H3CuLIQzMGPGfzk5ZRX3LRQcIwcvVZ+wOsLdQH/qHUgnGkQvLSXnJ0Yu1RX7ARIq3QnOZrByh9SDQX3UYd6y9dijFNlVZ3I2zp0OPQpg94BDgXdGZABj880mVcv1FsGEr36Njl66lfza8IJECxv0dG1DKFUgkFdS+IOW+dgnnjpA0blbq0qC/PM+R/sX2lY3loBgnrLQCrHIlaWGxFSDQZLY7sRsiLSij7vC3E4c5HD3tOsnF9Kyy6UUoSsa2O4EUCekZ9YlUNq2YgWIN3T8qOu/lAEdC3I4fFjzFGlFSzmBASCTeFadvzHU+Xl2izL7SrrI/U9U92H6rukeyt9xPIZdz1v/PdLSfNOHjV4pdlqCItVk53qqRKOGagNA4KRUepDsN9Yqw8Jan7bjeHxbmaa+EuJ3o5l1MVyfkgllbo6LItREw5NWIqiDQNi1qnjEz+3SXeXxyd2LPbXci9y4CyDQGS5W8dxycXfOGx9ztjv6bWf2EtRggkbu3ZsFiuBxWbi5X1VddAYrjeQPM/BodQB01kWzrpQZ9dwRFKvwiotjCQnhj1GZDK6Zrkrzyfdm/auB5CS/Z77haa7GClY3uITQXjzfa4kcCx9se9o3W80iQho2z4dOs4YM64buh5y5RwllaZp75DXmPbiwqQ/W4yQXC7v7vi92c0tKOtS9gogAoiAIoA0hgkgAogwAUQAESaANIn9C71gOzsIHclKAAAAAElFTkSuQmCC
>>>>>>>>>>>>>>>>>>>> 线段干扰验证码 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 圆圈干扰验证码 start <<<<<<<<<<<<<<<<<<<<
验证码结果:s024
验证码Base64编码:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAmCAYAAAAycj4zAAAFV0lEQVR42u2ZbUxbZRTH+e4XY0w0MTGLMTFZzGLUGMV98BUXjR8W3YyZiTPMbPNlcRuSbQkwtsUhDGRYjZvgGKMZ2xgTGIOyUQZYMkaB8DYoWGW8t/QVWvp+jz2H3Eawt9B7b9dKnn/zpHCfk957z+8+5zzn3CRgSiglMRcwIEwMCAPCxIAwIEwMCAPCtN6BcAEOuq/0QsXeq1Cw+SfIfek0fVd+Uw0D1+/Jc47gp3axHdKtZ+A94yF425AGW4zp8JXlNJQ5VODkXDG9x77uKshKe5xGQgPxeXyg3HUZcp7/QXCUfVoBbodH9DksgXnYbc6H1w37BccHc5kw7L0fk3s0zurgxOEN/w8gzYWt5PTC136Guxe6YNG29KQ6zE7oKNNC/qsKmlcXtIpeGTyMneYcuOnSgofzLT0MnB9a3b2wy5xH81vnMsAUsMt6fx63A4pykkMwEhqI1+WDvJeLKETN6c1hbca1EwQEQxiGtmhV7+ogZyMUNxd+lSGgfZYislMsXJP1Hi+dTyUI9dUZiQ/knkpHzq45Uh/R7uqBGrIbva2P+hxHbMXkaK1HF9EOwxXafWQ6Jtv93Wn7lQAUK94HjgtIB+IK+KFosg/e6q2FRzXn4OE/SuCx9lLYOtAAJTNDEABO0gXfyG4kRw/WD0e0G7wxRHaNOeqoz4EJHB3NreFa0S7F+K0sMMbHOiE7/QnIPfoszNtn6ZgkIFafG5J7quChIGWhsbnnGjj8XtEXfWXf7+Toie7JiHaTvdNkd3F3ZcxWK4YtBIIApcrhMEFe9iZyvn6kJXRcEpD9f2rI6bg6NPaZ0GrwBJde1dxfsLGzguaPjWlFXzgmcnQ07rRW24nxiT9WanJ1EZCjtlLJv1X6y4fk+ObGU8uOSwKCoQkdPi+wAnSLVppHMGKFCR0dvRah3alXfoxNHRT8pJpzCUifVy/pt9SqXHK6suST/8xJAvKI5jdyePfCXMyeSr7OWKst7sZioWJHHcHYo9sJ1Y1viP6dkaFb5PCCEy+A02mRF8i7/XUEBMHsDcbBatPflFfkFDo43kAaXHcJxvaxPbDILd2fGCg26xTkZDwDxw89CRP3u8LaSALSG0xMuLNamcif016Gr0fboMY0FiysApKcgSEoniGrzd1HMD42HadqHkGIgeEPhvUzhSnk7M7284J2kre9mCc+192m7W64XRbmGcVUv2iHKN45G7ekrnEPUD8L6w6p1XlNZRo5ulL5haBNZktPCAj+vXJEVRjiDqvFNg0nx7th26AqlF/4IRYKNg/R0YZhY0S7qf4Zsrv0ZZUsMNSuHnjTcFAWGP9+8qMdK4GJrtQRkNo6CSl9tZJ2WqrvmsjRQzdH1lTR12WpJDnOH7xybI3wrRRrYEEWwHIAiQSFgDzdoSRnz3icgheClTzaYEgTI13TKDn6emZDRDucRzsEI1aYI7DVjjCybOdCDcYHId7RkXLIqiuELwwP6tsFDTHx84le1BPr9Yeai9MDs4Lh6vsXC8nG4xTXFdD7pqiTizCUzlsQD0nOIXqXPZQrUnXNoF0whqp1bJeUG0Zgw51ymj87PSi+Qs5vCXVzsf3utCzScWzD4/94HOexTS9GBr+VYODLqNUajLGW5F0WtkiEdlj8wJUkRS67G0q2X4j4ggpfYK22ExPSSbsy4oupcCNhgfBbX6w7NgXDEq4YBPRURzl8NqymHpccwtClvdhDr3D52gS/cVeFr3bFvAfhhd3bdQWEKf5iQBiQ+Ch54zbBwYDEAcQyVexY9p1IYJLWO4ywWgFkVXsGhK0QlkNYDomjBEIWA/KAtSNlgEYiA/kH3fGHbpF0m+YAAAAASUVORK5CYII=
>>>>>>>>>>>>>>>>>>>> 圆圈干扰验证码 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 扭曲干扰验证码 start <<<<<<<<<<<<<<<<<<<<
验证码结果:ry5n
验证码Base64编码:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAmCAYAAAAycj4zAAAEgUlEQVR42u2Y/WtTZxTH+w8IY4iw32TsF3+RMZgv4A8iilK6/TIQGSJORWXiZCDbfhiILyC+/GC3laB2iC8VdU5rHVRkqGOzaZu+2Np2aZqkeWlzb5M0r22S5uY+x5xzSTRtkqbJvfVpeg885DZ5uL33+3nO+T7nqQE9uIoaXQIdyKJFKiJD9woLDR0IJxFqncpCWQpwqh6IcH5yDhCewSwLD7Ht9hCYTPAMh3sgrmEJ9q4V4PgOb9n36P/ERqJPdcRzvteBlBG3zoYJSMuVaEXG3rtqpOg8XqC8VyAHPxdBThWfIyUZHFovwr5PBQj75bJN3b7Ho++yisXPX/lo5Xf9HS86758HMZpXfyxQkbGL9QGYaAiCpW5MB5Ivmg1R8NgVf2g4HoRkguWdd2aPn+YMGmcqMnXMkp4PLNny1bNyRAeSL3760gf7PxOg7a+YJob+rqnjGD/pz173rbaB/RthjtkvayAPG6Ik+qWjAdUNfbZp+2+G6TphS+q7rELhNCdJdMySqbCcY+aHN5Zv5qWGHGcQfBwlKM7vJnQgGD/WeQkKGriaZr6QMG91cZEpZQGxBk+r+hB3LkZI/HMH3nbTp76u3MwXEkxi8P8mJzi+FZcOkC6xFrrFL2A0dFHVh7APJOH7bV4qT1i21DLzTLz6yFrSvMADpXRFjTG+gcgsAfbQeTAJ22jEJAdMJ62qPsjtC0qWPP9jGprOKWb+6HJUlXtbd42T0Ni1F+3qg0pXjzsvLoFkANiCZ+lzMv4Mk1uTBxnuniEIJ3b6qYNX08wn70aUXVZTuKTdGG6HFxod8g0aiwIER5ewXfPVcWzLBEHRwsxR6KENTvKKwqVAmdfz4UjZQERmhmkIgI291KZkZYC4IgbNgVw/E84CUdvMh3e4SWxLrbvgnMRosqSDyPmgdMo36bNXvp++vgUmuUl9IKFEp+ZAhjqVsoUli6nceki+FJi3uN6adp77ey8H6XfzZldFQHD0yc05fw/JTyh75iv5JQNhTNIcCDaDCMTwQ1CjLhDA1xjK6c6xMQzcj5Dhv15jV45XTvsrAvJaboEUSOBj1vS/lHJ+G5Rb09+lKgeyGIFlCoHkO9dSM7BsYVl69+hkcJ0jW66S41LZQNrl6yCwQYiAACFwp93EASIMwRh0wwBrgTb5SnoYwMxaoZ/9SaOL3QATuwYv2W98AcETYASyWIHm3fexDRyHRYLhNoyD595YVkwvDJOYAvSDk7XDaNqoLexpQTH/ZfUVD26A4A4LvWMmXv62eia9v4lDeI6gbjCRoCPsGQk6wJo1EbMqgMSmGPg9KaURvOZd8oJyA6TQ6iyU7q/YXRKzdawRXsR/rRpB/2O/0Hvh++F74uLB98bFhDrg4mpjjWBkv5NOOGJpp0Ht0Oxr5kt1Y+QIGKNH84pZbauzg13NERPfd7aYqAvqk0/MUrv1YvNqqkXMYoJidqKgmK0oKO58ZoupRlQdkPctaFUBWWi6Y3lEMXkSlBsgxVYmeshyEJMbILqMfIHTgehA9NCBcApFB7IE4g2ZJfukmovrtAAAAABJRU5ErkJggg==
>>>>>>>>>>>>>>>>>>>> 扭曲干扰验证码 end <<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> 自定义纯数字验证码 start <<<<<<<<<<<<<<<<<<<<
验证码结果:7231
验证码Base64编码:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAmCAYAAAAycj4zAAAEeElEQVR42u2Zf2hTVxTH/Wv/60CcwyH+YhMHmyLDgYqrMMSx/bExxpy/1tY6q65qpVCx1DI7NtbN1a7rVhV/zCkMZUhdbZnSakZV2qQ/kqatSdPGtr4kTfOz+f3eMefMhHY0mXl575Ho/cL9I3mHe1/OJ+fce86dBUwZpVnMBQwIEwPCgDAxIAzIc62axc0pDQYkS/TpnxsYkIwHMtsyL+UhVkJ0tDyyQ1WPCb74Rwd5d7RQoNLB8U4jXBu2gC/Cp/0jvREnXBn7AUp7N8PnmuWwXb0MirXvwHlzOXCBIdmce73qHFTm5CkPZIltuagXdgXDcExtgJ23exKOort6GHT7RDvF7NPDvu418FnHohkHwmkd/0NyGG2XGgmGJECeVrWTv8JL1oWgDmlERUYMRmn7ALRZHBDiBXoWFgRot7mgrOMBPf+yTQ/OKLxU5Qk7oLD7LXJ8lSEf+jz3o+v+G3EYGZdHvomDwWdSiI9GdFP1xTiMVIGI3tT/Ctyg6LjsF/fvusNNkLMRSpCfOS0hoMpo6kK7341jKa8Rc/jPpoMJbRotZ8imcmBL2jA4gxlO7To2DYYiQIyRQVhgXQL5rj2iF/pRO0SO1jk8Se0wXaFd8b2+lNc4qF1PzrYFRxLaRIQI2exQvyr6tzzqH4KrFXXTIFw48K1yQHImNsEy2wqw8xOiF8INHB0tPIUt2uWrtLJsvEHeR0ByNStEzzEVRPXHxdDVqJr2vaxATk7WUqr6zX9JkWMgpi0EggDlEJ6+EEj9UElaQE4XVICmoZX2kP+Ckg0Ix3PwsnURrLXnKHYuv2t1EpDaXrNkc+LGrnWr4HvDLoKxv/ttcIZsouezGB8mjRzZgOx1HaDouBVsUQQGprOjT05a/U6vJOkJ94qpx96v+j+h05gckhWIIWIkGOvtGxWLjismjmD80vdQkvm6XK1wRP8eRQaCwBoEoRzVvw+jfkN2ATnkLknrmJuqVJyDYGAtEpCgWp9J7rAd6kyHCMqertVppS1FgXgFL8yzvkInKx542WF0jLsIxuH7/VTNy63qwb0EBVspWQHkgu8iRUepp0x252jG3dTPwrrDqQAMFKar2OaeFUA+cHxEQNpDHbI65l70RJWrMIyY0i0OZQOyyvwTTB0rzSdgjmU+vMgtpM9yiBcEao3EWinukDQw8jpfJ0dPhLikdmEhRHZon/ER0hBopOjIde2WBQbuEcef9KtqeofjDUYpdMK4mxzdZDmb1K7d0Ux23xlyMx9ImaeCgNT56iWHYfb6qZOLMBrMVsnnVztvkqOLetaBn59MWJuU6N4lOwST8UC2OHcQkObg35K+rD0QIhh4GfV/DcZ0hG13dHZ534egc7fF2++YphAAXlrh85rB/dlRh7xme4OAjERGJX3Z+mixl+xiaqYhRhgZ2FpPdEGFo9ZURICyAshcywICInX9gd1bJYDEC037Vfh6YCtd4W5TL4WCzjcpKjBq5JKiF1RMyogBYUCYGBAGhIkBYUCYGBAG5NnRC4WraWS6HgOq7ECdHAdfZQAAAABJRU5ErkJggg==
>>>>>>>>>>>>>>>>>>>> 自定义纯数字验证码 end <<<<<<<<<<<<<<<<<<<<

1.5.7、拓展

背景: 上面解释了如何获取验证码结果以及验证码Base64编码字符串,其实我们还可以直接把验证码结果输出到响应流中,请看下面代码

代码:

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;@GetMapping("/previewCaptcha")
public void previewCaptcha(HttpServletResponse response) throws IOException {try (ServletOutputStream outputStream = response.getOutputStream();) {// 将验证码图片返回到前端response.setContentType("image/png");LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 38, 4, 4);// 将验证码图片返回captcha.write(outputStream);// 将验证码结果存储到redis中,具体过程省略String result = captcha.getCode();// 存储到redis中……} catch (Exception e) {e.printStackTrace();}
}

结果:

在这里插入图片描述

1.6、实战

1.6.1、使用场景

  • 用户登录
  • 注册账户

1.6.2、用途讲解

1.6.2.1、流程串讲

大家都看过验证码页面,比如ruoyi的登录页面:

在这里插入图片描述

所以需要用户进入该页面时就能把验证码给展示出来,这涉及到验证码图片获取接口,我已经在上面讲述了如何生成验证码图片的base64编码

考虑到用户需要提交用户输入验证码结果进行比对,我们在生成验证码的时候需要把验证码结果存储起来,这一般使用两种方式

  • 存储到session中:由于在分布式微服务应用中很少使用共享session方式了,所以这种一般不再使用
  • 存储到redis中:在存储的时候,我们可以把uuid和验证码结果进行绑定存储到redis中,当把验证码结果返回给用户的时候,同时把和验证码结果返回的uuid返回给用户

下面以把数据存储到redis中的方式进行讲解后续的步骤

以登录场景为例,用户输入用户名、密码、验证码之后点击登录按钮,由于我们之前已经把和验证码一一对应的uuid返回给用户,所以前端同事在提交数据的时候需要也需要把uuid提交到后端,进而后端依次完成验证码、登录信息的校验

这时候流程走到了后端代码中,我们本次讲解验证码的验证过程,可以分为这么几种情况

  • 网关层校验:适合分布式微服务代码,在gateway网关层完成验证码的校验工作,比如:ruoyi-cloud
  • 过滤器校验:都挺适合,可以把过滤器代码放在网关模块中完成校验工作
  • 直接校验:都挺适合,但是不够优雅,比如:renren-security
1.6.2.2、后端验证码校验—网关层校验

本次以ruoyi-cloud为例讲解

网关层拦截获取验证码请求:

作用:在网关层完成验证码获取工作

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import com.ruoyi.gateway.handler.ValidateCodeHandler;/*** 路由配置信息* * @author ruoyi*/
@Configuration
public class RouterFunctionConfiguration
{@Autowiredprivate ValidateCodeHandler validateCodeHandler;@SuppressWarnings("rawtypes")@Beanpublic RouterFunction routerFunction(){return RouterFunctions.route(RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),validateCodeHandler);}
}

处理获取验证码请求:

作用:获取验证码,一方面将验证码和对应uuid返回给前端,另外一方面把uuid和验证码进行绑定并存储在redis中

import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.gateway.service.ValidateCodeService;
import reactor.core.publisher.Mono;/*** 验证码获取** @author ruoyi*/
@Component
public class ValidateCodeHandler implements HandlerFunction<ServerResponse>
{@Autowiredprivate ValidateCodeService validateCodeService;@Overridepublic Mono<ServerResponse> handle(ServerRequest serverRequest){AjaxResult ajax;try{ajax = validateCodeService.createCaptcha();}catch (CaptchaException | IOException e){return Mono.error(e);}return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));}
}

生成验证码和校验验证码接口:

作用:为上面提供生成验证码服务、当用户提交登录、注册请求时,用于校验验证码是否正确

import java.io.IOException;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.common.core.web.domain.AjaxResult;/*** 验证码处理** @author ruoyi*/
public interface ValidateCodeService
{/*** 生成验证码*/public AjaxResult createCaptcha() throws IOException, CaptchaException;/*** 校验验证码*/public void checkCaptcha(String key, String value) throws CaptchaException;
}

生成验证码和校验验证码实现类:

import com.google.code.kaptcha.Producer;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.sign.Base64;
import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.CaptchaProperties;
import com.ruoyi.gateway.service.ValidateCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;/*** 验证码实现处理** @author ruoyi*/
@Service
public class ValidateCodeServiceImpl implements ValidateCodeService
{@Resource(name = "captchaProducer")private Producer captchaProducer;@Resource(name = "captchaProducerMath")private Producer captchaProducerMath;@Autowiredprivate RedisService redisService;@Autowiredprivate CaptchaProperties captchaProperties;/*** 生成验证码*/@Overridepublic AjaxResult createCaptcha() throws IOException, CaptchaException{AjaxResult ajax = AjaxResult.success();boolean captchaEnabled = captchaProperties.getEnabled();ajax.put("captchaEnabled", captchaEnabled);if (!captchaEnabled){return ajax;}// 保存验证码信息String uuid = IdUtils.simpleUUID();String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;String capStr = null, code = null;BufferedImage image = null;String captchaType = captchaProperties.getType();// 生成验证码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);}redisService.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;}/*** 校验验证码*/@Overridepublic void checkCaptcha(String code, String uuid) throws CaptchaException{if (StringUtils.isEmpty(code)){throw new CaptchaException("验证码不能为空");}if (StringUtils.isEmpty(uuid)){throw new CaptchaException("验证码已失效");}String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;String captcha = redisService.getCacheObject(verifyKey);redisService.deleteObject(verifyKey);if (!code.equalsIgnoreCase(captcha)){throw new CaptchaException("验证码错误");}}
}

拦截登录、注册请求,校验验证码是否正确过滤器:

作用:拦截登录、注册请求,统一校验验证码是否正确

/*** 验证码过滤器** @author ruoyi*/
@Component
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
{private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" };@Autowiredprivate ValidateCodeService validateCodeService;@Autowiredprivate CaptchaProperties captchaProperties;private static final String CODE = "code";private static final String UUID = "uuid";@Overridepublic GatewayFilter apply(Object config){return (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();// 非登录/注册请求或验证码关闭,不处理if (!StringUtils.containsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()){return chain.filter(exchange);}try{String rspStr = resolveBodyFromRequest(request);JSONObject obj = JSON.parseObject(rspStr);validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));}catch (Exception e){return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());}return chain.filter(exchange);};}private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){// 获取请求体Flux<DataBuffer> body = serverHttpRequest.getBody();AtomicReference<String> bodyRef = new AtomicReference<>();body.subscribe(buffer -> {CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());DataBufferUtils.release(buffer);bodyRef.set(charBuffer.toString());});return bodyRef.get();}
}
1.6.2.3、后端验证码校验—直接校验

本次以renren-security为例讲解

获取验证码方法:

import io.renren.common.exception.ErrorCode;
import io.renren.common.validator.AssertUtils;
import io.renren.modules.security.service.CaptchaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 登录* * @author Mark sunlightcs@gmail.com*/
@RestController
@Api(tags="登录管理")
public class LoginController {@Autowiredprivate CaptchaService captchaService;@GetMapping("captcha")@ApiOperation(value = "验证码", produces="application/octet-stream")@ApiImplicitParam(paramType = "query", dataType="string", name = "uuid", required = true)public void captcha(HttpServletResponse response, String uuid)throws IOException {//uuid不能为空AssertUtils.isBlank(uuid, ErrorCode.IDENTIFIER_NOT_NULL);//生成验证码captchaService.create(response, uuid);}
}

获取验证码和校验验证码接口:

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 验证码** @author Mark sunlightcs@gmail.com*/
public interface CaptchaService {/*** 图片验证码*/void create(HttpServletResponse response, String uuid) throws IOException;/*** 验证码效验* @param uuid  uuid* @param code  验证码* @return  true:成功  false:失败*/boolean validate(String uuid, String code);
}

获取验证码和校验验证码实现类:

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import io.renren.common.redis.RedisKeys;
import io.renren.common.redis.RedisUtils;
import io.renren.modules.security.service.CaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;/*** 验证码** @author Mark sunlightcs@gmail.com*/
@Service
public class CaptchaServiceImpl implements CaptchaService {@Autowiredprivate RedisUtils redisUtils;@Value("${renren.redis.open: false}")private boolean open;/*** Local Cache  5分钟过期*/Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(5, TimeUnit.MINUTES).build();@Overridepublic void create(HttpServletResponse response, String uuid) throws IOException {response.setContentType("image/gif");response.setHeader("Pragma", "No-cache");response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expires", 0);//生成验证码SpecCaptcha captcha = new SpecCaptcha(150, 40);captcha.setLen(5);captcha.setCharType(Captcha.TYPE_DEFAULT);captcha.out(response.getOutputStream());//保存到Redis缓存setCache(uuid, captcha.text());}@Overridepublic boolean validate(String uuid, String code) {//获取验证码String captcha = getCache(uuid);//效验成功if(code.equalsIgnoreCase(captcha)){return true;}return false;}private void setCache(String key, String value){if(open){key = RedisKeys.getCaptchaKey(key);redisUtils.set(key, value, 300);}else{localCache.put(key, value);}}private String getCache(String key){if(open){key = RedisKeys.getCaptchaKey(key);String captcha = (String)redisUtils.get(key);//删除验证码if(captcha != null){redisUtils.delete(key);}return captcha;}String captcha = localCache.getIfPresent(key);//删除验证码if(captcha != null){localCache.invalidate(key);}return captcha;}
}

在登陆接口中校验验证码是否正确:

import io.renren.common.exception.ErrorCode;
import io.renren.common.validator.AssertUtils;
import io.renren.modules.security.service.CaptchaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** 登录* * @author Mark sunlightcs@gmail.com*/
@RestController
@Api(tags="登录管理")
public class LoginController {@Autowiredprivate CaptchaService captchaService;@PostMapping("login")@ApiOperation(value = "登录")public Result login(HttpServletRequest request, @RequestBody LoginDTO login) {//效验数据ValidatorUtils.validateEntity(login);//验证码是否正确boolean flag = captchaService.validate(login.getUuid(), login.getCaptcha());if(!flag){return new Result().error(ErrorCode.CAPTCHA_ERROR);}//用户信息SysUserDTO user = sysUserService.getByUsername(login.getUsername());SysLogLoginEntity log = new SysLogLoginEntity();log.setOperation(LoginOperationEnum.LOGIN.value());log.setCreateDate(new Date());log.setIp(IpUtils.getIpAddr(request));log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));log.setIp(IpUtils.getIpAddr(request));//用户不存在if(user == null){log.setStatus(LoginStatusEnum.FAIL.value());log.setCreatorName(login.getUsername());sysLogLoginService.save(log);throw new RenException(ErrorCode.ACCOUNT_PASSWORD_ERROR);}//密码错误if(!PasswordUtils.matches(login.getPassword(), user.getPassword())){log.setStatus(LoginStatusEnum.FAIL.value());log.setCreator(user.getId());log.setCreatorName(user.getUsername());sysLogLoginService.save(log);throw new RenException(ErrorCode.ACCOUNT_PASSWORD_ERROR);}//账号停用if(user.getStatus() == UserStatusEnum.DISABLE.value()){log.setStatus(LoginStatusEnum.LOCK.value());log.setCreator(user.getId());log.setCreatorName(user.getUsername());sysLogLoginService.save(log);throw new RenException(ErrorCode.ACCOUNT_DISABLE);}//登录成功log.setStatus(LoginStatusEnum.SUCCESS.value());log.setCreator(user.getId());log.setCreatorName(user.getUsername());sysLogLoginService.save(log);return sysUserTokenService.createToken(user.getId());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/144442.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DE0开发板交通灯十字路口红绿灯VHDL

名称&#xff1a;基于DE0开发板的交通灯十字路口红绿灯 软件&#xff1a;Quartus 语言&#xff1a;VHDL 要求&#xff1a; 设计一个十字路口交通信号灯的控制电路。分为两种情况&#xff0c;正常状态和报警状态。 1.正常状态&#xff1a;要求红、绿灯按一定的规律亮和灭&a…

触觉智能 PurPle Pi OH(OpenHarmony)开发板

资料汇总 内容预览 产品介绍 PurPle-Pi OH 规格书​​​​​​ 系统编译 Purple-Pi-OH Linux SDK编译 Purple-Pi-OH OHOS SDK编译 使用手册 Purple-Pi-OH Ubuntu系统使用手册 常见FAQ 常见问题 官网 官网地址 Purple Pi OH介绍 Purple Pi OH作为一款兼容树莓派的开…

多个pdf合并成一个文件,3个方法合并pdf

如何把多个pdf合并成一个文件&#xff1f;在我们日常的工作中&#xff0c;经常会遇到一些需要处理的文件&#xff0c;其中包括PDF文件。特别是当我们需要将多个PDF文件合并成一个PDF文件时&#xff0c;会面临一些困难。这样的情况下&#xff0c;我们的阅读能力会受到限制&#…

软件测试中的测试工具和自动化测试

1. 测试工具 测试工具也分为不同人员使用的 开发人员&#xff1a;测试框架&#xff0c;编写测试用例&#xff1b;各类线上dump分析工具如windgb&#xff1b;开发时的集成IDE工具如Visual Studio&#xff0c;idea等等 面向不同测试需求的测试工具 软件测试是软件开发生命周期…

手摸手带你 在Windows系统中安装Istio

Istio简介 通过负载均衡、服务间的身份验证、监控等方法&#xff0c;Istio 可以轻松地创建一个已经部署了服务的网络&#xff0c;而服务的代码只需很少更改甚至无需更改。 通过在整个环境中部署一个特殊的 sidecar 代理为服务添加 Istio 的支持&#xff0c;而代理会拦截微服务…

Multisim14.0仿真(二十七)基于UC3842的反激式开关电源的设计及仿真

一、UC3842简介&#xff1a; UC3842为固定频率电流模式PWM控制器。它们是专门为OFF−线和直流到直流转换器应用与最小的外部组件。内部实现的电路包括用于精确占空比控制的修剪振荡器、温度补偿参考、高增益误差放大器、电流传感比较器和理想适合于驱动功率MOSFET的高电流温度极…

MySQL学习笔记24

MySQL的物理备份&#xff1a; xtrabackup备份介绍&#xff1a; xtrabackup优缺点&#xff1a; 优点&#xff1a; 1、备份过程快速、可靠&#xff08;因为是物理备份&#xff09;&#xff1b;直接拷贝物理文件。 2、支持增量备份&#xff0c;更为灵活&#xff1b; 3、备份…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 C: 班级活动

[蓝桥杯 2023 国 B] 班级活动 【问题描述】 小明的老师准备组织一次班级活动。班上一共有 n n n 名&#xff08; n n n 为偶数&#xff09;同学&#xff0c;老师想把所有的同学进行分组&#xff0c;每两名同学一组。为了公平&#xff0c;老师给每名同学随机分配了一个 n n …

Eclipse环境基于HDFS的API进行开发

文章目录 IOUtils方式读取文件1.文件准备2.下载安装Eclipse3.打开eclipse&#xff0c;新建java项目&#xff0c;添加关于hadoop的一些包4.包内新建类进行开发5.利用打包的方式生成java jar包6.验证代码正确性 其它问题&#xff1a;Exception in thread “main“ java.lang.Unsu…

【前端】ECMAScript6从入门到进阶

【前端】ECMAScript6从入门到进阶 1.ES6简介及环境搭建 1.1.ECMAScript 6简介 &#xff08;1&#xff09;ECMAScript 6是什么 ECMAScript 6.0&#xff08;以下简称 ES6&#xff09;是 JavaScript 语言的下一代标准&#xff0c;已经在2015年6月正式发布了。它的目标&#xff…

基于微信小程序的刷题考试系统设计与实现(适用于各类考试类、答题类程序)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

【IDEA】使用idea调试时查看对象集合的值

1、在实体类上添加toString方法 2、在要查看集合的地方右键View as→toString 3、View Text复制对象集合的值 4、复制map集合的值同理

[杂谈]-ESP32中的无线通信协议

ESP32中的无线通信协议 文章目录 ESP32中的无线通信协议1、ESP32 无线通信协议简介2、Bluetooth Low Energy (BLE)3、**Bluetooth Classic**4、**ESP-NOW**5、Wi-Fi&#xff08;客户端-服务器通信协议&#xff09;6、MQTT7、**LoRa**8、**GSM/GPRS/LTE**9、总结 ESP32是一个基于…

如何使用ArcGIS Pro直接获取道路中心线

以前使用ArcGIS获取道路中心线&#xff0c;需要先将面要素转换为栅格再获取中心线&#xff0c;现在我们可以通过ArcGIS Pro直接获取道路中心线&#xff0c;这里为大家介绍一下获取方法&#xff0c;希望能对你有所帮助。 新建地理数据库 在存储数据的文件夹上点击右键&#xff…

专栏更新情况:华为流程、产品经理、战略管理、IPD

目录 前言 01 华为流程体系入门课 CSDN学院 02 产品经理进阶课 CSDN学院 03 BLM 战略方法论进阶课 04 IPD 进阶 100 例专栏 作者简介 前言 已上线四大课程专栏更新情况&#xff1a; 01 华为流程体系入门课&#xff08;视频图文&#xff09;&#xff1b; 02 硬件产品经…

Linux文件查找,别名,用户组综合练习

1.文件查看: 查看/etc/passwd文件的第5行 [rootserver ~]# head -5 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi…

使用YOLOv5的backbone网络识别图像天气 - P9

目录 环境步骤环境设置包引用声明一个全局的设备 数据准备收集数据集信息构建数据集在数据集中读取分类名称划分训练、测试数据集数据集划分批次 模型设计编写维持卷积前后图像大小不变的padding计算函数编写YOLOv5中使用的卷积模块编写YOLOv5中使用的Bottleneck模块编写YOLOv5…

信息安全:网络物理隔离技术原理与应用.

信息安全&#xff1a;网络物理隔离技术原理与应用. 随着网络攻击技术不断增强&#xff0c;恶意入侵内部网络的风险性也相应急剧提高。满足内外网信息及数据交换需求&#xff0c;又能防止网络安全事件出现的安全技术就应运而生了&#xff0c;这种技术称为“物理隔离技术” 基本原…

企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…

照片后期处理软件DxO FilmPack 6 mac中文说明

DxO FilmPack 6 for Mac是一款照片后期处理软件。它可以模拟超过60种著名胶片品牌和类型的色彩和颗粒感&#xff0c;使照片具有复古、艺术和时尚风格。 ​DxO FilmPack 6 mac支持RAW和JPG格式的照片&#xff0c;并提供丰富的调整选项&#xff0c;如亮度、对比度、曝光、阴影和高…