目录
1.验证码的实现过程
1.1代码需要引入的依赖:
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
1.2代码所在的包:
package com.ruoyi.web.controller.common;
1.3代码的映射路径以及方法名称:
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException{
}
1.4代码的深入逻辑:
1.4.1:检查验证码开关
boolean captchaEnabled = configService.selectCaptchaEnabled();
selectCaptchaEnabled():检查验证码开关 -true:开 -false:关
通过selectCaptchaEnabled()方法中的
selectConfigByKey(String configKey)
在调用redisCache.getCacheObject(getCacheKey(configKey)),可以获取到在reids缓存的对应的value值。其中有两种情况:
第一种:从redis成功获取到了value值:
/**
* 在redis中储存验证码的形式为:sys_config:sys.account.captchaEnabled :true
* 或者sys_config:sys.account.captchaEnabled :false 这种形式
*/
String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)));
private String getCacheKey(String configKey)
{
//在redis中储存验证码的key为:sys_config:sys.account.captchaEnabled
return CacheConstants.SYS_CONFIG_KEY + configKey;
}
第二种(redis获取失败,从mysql获取):
if (StringUtils.isNotEmpty(configValue))
{
//找到value直接返回
return configValue;
}
SysConfig config = new SysConfig();
config.setConfigKey(configKey);
SysConfig retConfig = configMapper.selectConfig(config);
if (StringUtils.isNotNull(retConfig)){
redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue());
return retConfig.getConfigValue();
}
在mysql查找时,设置了自定义的SysConfigMapper.xml文件, 并在 sys_config 表中根据 configKey来查找对应的value。
若返回结果不为null:则将对应的key和value 存入redis缓存中,即:key: sys_config:sys.account.captchaEnabled -> value: true(false);
若返回结果为null: 则返回结果为“” (空字符串),在利用Convert.toBool(captchaEnabled)返回boolean类型的值。
补充:
String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled");
//假如redis缓存查不到,则开启验证码,保证安全性
if(StringUtils.isEmpty(captchaEnabled)){
return true;
}
从上述代码我们可以看出,假如查找完验证码为空,则返回null,也就是说假如验证码未设置开和关(yes or no || 1 or 0)这种关系,则默认是开启的,目的是保证了安全性。
通俗的讲:除非我明确在redis或者mysql表示了我的验证码状态时关闭的,否则都是开启的状态。
1.4.2:保存验证码:
//生成key 形式为:captcha_codes: 随机生成的uuid
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
@ConfigurationProperties(prefix = "ruoyi")根据配置文件中前缀为“ruoyi”来获取属性值
在application.yml中表现为:
ruoyi:
captchaType: math
其中captchaType属性可以又两个值:1.math 数字验证码 2.char 字符验证码
若为math,则:
String capText = captchaProducerMath.createText();
//createText()中会调用getText()方法
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
public String getText()
{
Integer result = 0;
Random random = new Random();
//x和y的值都是10以内随机数
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
//randomoperands的值为3以内的随机数,randomoperands的作用是随机分配乘、除、加、减四个算法
int randomoperands = random.nextInt(3);
if (randomoperands == 0)
{
//在此就解释一个,一下可以类推
//suChinese会拼接上x和y的值 也就是这种形式:例如x = 3,y = 4;则为: 3 * 4
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继续拼接=?@,拼接后最后形式为: 3 * 4 =?@12
suChinese.append("=?@" + result);
//最后返回的就是拼接的最后结果:3 * 4 =?@12
return suChinese.toString();
}
后续利用
//将结果中的@符号去掉。变成 3 * 4 =?这种形式
capStr = capText.substring(0, capText.lastIndexOf("@"));
//截取结果 3 * 4 =?12 ,最后变成算式结果12
code = capText.substring(capText.lastIndexOf("@") + 1);
//生成图片验证码
image = captchaProducerMath.createImage(capStr);
最后将生成的验证码放入redis缓存中,并设置默认的过期时间
verifyKey:captcha_codes: 随机生成的uuid code:最后算式的结果 Constants.CAPTCHA_EXPIRATION:过期时间 默认为两分钟
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION,TimeUnit.MINUTES);
转换流信息将image写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try
{
ImageIO.write(image, "jpg", os);
}
catch (IOException e)
{
return AjaxResult.error(e.getMessage());
}
将uuid和image放入ajax中返回给前端
ajax.put("uuid", uuid);
//通过base64将流转换成字节数组
ajax.put("img", Base64.encode(os.toByteArray()));
综上所述:
ajax所携带的信息有五个:
1.验证码开关状态:-true 或者 -false
2.uuid 唯一标识
3.image 验证码的.jpg图片
4.msg 返回信息:操作成功
5.code 状态码:200