当前位置:   article > 正文

基于SpringBoot实现验证码功能_springboot登陆的时候验证算数验证码

springboot登陆的时候验证算数验证码

目录

一 实现思路

二 代码实现

三 代码汇总


现在的登录都需要输入验证码用来检测是否是真人登录,所以验证码功能在现在是非常普遍的,那么接下来我们就基于springboot来实现验证码功能。

一 实现思路

        今天我们介绍的是两种主流的验证码,一种就是上图所示的需要进行计算的验证码,另外一种就是不需要计算,直接输入的验证码。 

1.如果验证码的类型是一个计算类型验证码

* 那么我们就需要分析一下:
* 比如一个计算类型的验证码:1+2=?
* 那么我们到时候应该填入的验证码的答案是:3
* 所以这个验证码它包含了两个部分,一个是"1+2=?" 另外一个是"3"
* 其实生成这个计算问题是我们自己来实现的,我们的工具类已经封装好了计算提,比如:“1+2=3”
* 所以我们现在要做的主要有三步:
* 1.得到这个算式后,将算是“1+2=3”分割成两个部分,“1+2=?” 和 “3”
* 2.然后我们需要把它们存储到缓存中去,前面可以当作key,后面可以当作value,然后进行验证答案的正确性
* 3.最后,我们需要把“1+2=?”制作成一个图片返回给前端,用于展示给用户,

 2.如果验证码的类型是一个普通的图形验证码

    那么我们不要分为表达式和答案两个部分,我们只需要把生成的这个图形直接存入缓存中去就可以了。

    但是因为我们这两种类型是在同一个类中进行判断的,所以最好还是用两个变量来接收。

上面这两中类型的验证码判断完成之后,不管是那种类型,最后都需要把数据存到缓存中,并且都会生成一个Base64编码的一个图片,我们只需要返回这个图片即可,还需要一个uuid,因为这个是用来作为key的唯一标识。

二 代码实现

开始代码之前,先介绍一下我注入的几个bean:

  1. @Autowired
  2. private ISysConfigService configService ; //判断是否开启验证码
  3. @Autowired
  4. private FeiSiConfig feiSiConfig ; //配置信息
  5. @Autowired
  6. private KaptchaTextCreator kaptchaTextCreator ; //随机生成验证码表达式
  7. @Autowired
  8. private RedisCache redisCache ; //存储数据到缓存
  9. @Resource(name = "captchaProducer")
  10. private Producer captchaProducer; //生成普通类型验证码图片
  11. @Resource(name="captchaProducerMath")
  12. private Producer captchaProducerMath; //生成计算类型验证码图片

① 先判断有没有开启验证码功能。

我们有一个网页功能的数据库表,里面可以选择是否开启验证码功能,并且在类加载的时候,我们就已经使用 @PostConstruct(标记在方法上, 当这个bean创建完成,自动执行这个注解标记方法,要求这个方法无参 类似 init-method配置) 注解将数据库的数据缓存在了redis里面。

所以我们可以判断先判断有没有开启验证码功能:

  1. // 先要判断一下是否开启了验证码功能,如果没有开启,则不需要进行生成验证码的操作了
  2. boolean captchaEnabled = configService.selectCaptchaEnabled();
  3. if (!captchaEnabled){
  4. return ajaxResult.put("captchaOnOff",captchaEnabled) ; //返回没有开启验证码信息给前端
  5. }
configService这个类就是我们用来初始化缓存数据的,这个类代码如下:
  1. package com.fs.system.service.ipml;
  2. import cn.hutool.core.convert.Convert;
  3. import cn.hutool.core.util.StrUtil;
  4. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  5. import com.fs.common.constant.CacheConstants;
  6. import com.fs.common.core.pojo.SysConfig;
  7. import com.fs.common.util.RedisCache;
  8. import com.fs.common.util.StringUtils;
  9. import com.fs.system.mapper.SysConfigMapper;
  10. import com.fs.system.service.ISysConfigService;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import javax.annotation.PostConstruct;
  14. import java.util.List;
  15. import java.util.Objects;
  16. @Service
  17. public class SysConfigServiceImpl implements ISysConfigService {
  18. @Autowired
  19. private SysConfigMapper sysConfigMapper;
  20. @Autowired
  21. private RedisCache redisCache;
  22. @PostConstruct //标记在方法上, 当这个bean创建完成,自动执行这个注解标记方法,要求这个方法无参 类似 init-method配置
  23. public void init(){
  24. loadingConfigCache();
  25. }
  26. // 初始化数据,当系统加载的时候,把数据存入到缓存中去
  27. @Override
  28. public void loadingConfigCache() {
  29. System.out.println("初始化数据...");
  30. //查询数据库
  31. List<SysConfig> sysConfigs = sysConfigMapper.selectList(null);
  32. //保存到redis缓存中
  33. sysConfigs.forEach((sysConfig)->{
  34. redisCache.setCacheObject(getCacheKey(sysConfig.getConfigKey()),sysConfig.getConfigValue());
  35. });
  36. }
  37. /**
  38. * 构建参数配置表的redis的key
  39. * @param configKey
  40. * @return
  41. */
  42. // 获取redis缓存中的key
  43. private String getCacheKey(String configKey){
  44. return CacheConstants.SYS_CONFIG_KEY+configKey;
  45. }
  46. // 判断账号的验证码是否以及开启
  47. @Override
  48. public boolean selectCaptchaEnabled() {
  49. String cacheObject = redisCache.getCacheObject("sys.account.captchaOnOff");
  50. if (StrUtil.isBlank(cacheObject)){
  51. // 如果查询出来的是空,则说明该账号是第一次登录,则默认开启验证码
  52. return true ;
  53. }
  54. // 如果不是空,那么查询到的数据不是true就是false,但是存到数据库的并不是Boolean类型而是String类型,
  55. // 所以我们借用工具直接把字符串转成对应的Boolean类型
  56. return Convert.toBool(cacheObject) ;
  57. }
  58. // 根据账号的key获取缓存中的value
  59. @Override
  60. public String selectConfigByKey(String configKey) {
  61. // 1. 如果从redis中得到了数据,那么直接返回
  62. String cacheObject = redisCache.getCacheObject(getCacheKey(configKey));
  63. // 我们需要返回的是一个String类型,所以需要用工具包转为String类型
  64. String toStr = Convert.toStr(cacheObject);
  65. if (StrUtil.isNotBlank(toStr)){
  66. return toStr ;
  67. }
  68. // 2.如果没有得到数据,那么我们需要从sys_config数据库表中查询数据,同时把查询到的数据存入缓存中
  69. LambdaQueryWrapper<SysConfig> queryWrapper=new LambdaQueryWrapper<>();
  70. queryWrapper.eq(SysConfig::getConfigKey,configKey);
  71. SysConfig sysConfig = sysConfigMapper.selectOne(queryWrapper);
  72. // 如果查到有数据,就存入redis并且返回
  73. if(Objects.nonNull(sysConfig)){ //这里判空的方法不能继续和上面一样,因为sysConfig并不是字符串类型,而是一个对象
  74. // 获取到值
  75. String configValue = sysConfig.getConfigValue();
  76. redisCache.setCacheObject(getCacheKey(configKey),configValue);
  77. return configValue;
  78. }
  79. // 否则没有查到数据,则说明没有信息
  80. return null ;
  81. }
  82. }

在进行正式写逻辑代码之前,我们需要引入几个变量。

按照我们上面分析的,如果是一个计算类型的验证码,那么我们一个需要四个变量:

一个变量是表达式的前半部分,一个变量是表达式的答案,

一个变量是用于存储生成的验证码图片的Base64编码,

最后一个就是验证码数据存储在redis作为唯一标识key的uuid

  1. BufferedImage image = null ;//图片
  2. String expression ; //表达式部分
  3. String answer ; //答案部分
  4. String uuid = UUID.randomUUID().toString();; //uuid

② 判断开启后,我们接下来需要判断使用的是哪一种验证码,具体使用哪一种是我们自己配置好的,我们规定math是计算类型的验证码,char是普通验证码。

  1. /**
  2. * 至于这个captchaType的值是根据配置文件设置的,因为这个FeisiConfig是一个配置类
  3. * 它加了‘@ConfigurationProperties(prefix = "fs")’这个注解,
  4. * 而配置文件规定math是计算类型验证码,char是图形类型验证码
  5. */
  6. String captchaType = feiSiConfig.getCaptchaType();

③ 如果是计算类型的验证码,那么我们就需要按照以下步骤走:

1.首先,得到生成的计算表达式(由我们封装的工具类生成)

  1. // 获取一个随机生成的表达式(随机生成的工具封装在KaptchaTextCreator类中)
  2. String text = kaptchaTextCreator.getText();

  生成表达式的工具类代码如下:

  1. package com.fs.system.util;
  2. import com.google.code.kaptcha.text.impl.DefaultTextCreator;
  3. import org.springframework.stereotype.Component;
  4. import java.util.Random;
  5. @Component
  6. public class KaptchaTextCreator extends DefaultTextCreator {
  7. private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
  8. @Override
  9. public String getText()
  10. {
  11. Integer result = 0;
  12. Random random = new Random();
  13. int x = random.nextInt(10);
  14. int y = random.nextInt(10);
  15. StringBuilder suChinese = new StringBuilder();
  16. int randomoperands = random.nextInt(3);
  17. if (randomoperands == 0)
  18. {
  19. result = x * y;
  20. suChinese.append(CNUMBERS[x]);
  21. suChinese.append("*");
  22. suChinese.append(CNUMBERS[y]);
  23. }
  24. else if (randomoperands == 1)
  25. {
  26. if ((x != 0) && y % x == 0)
  27. {
  28. result = y / x;
  29. suChinese.append(CNUMBERS[y]);
  30. suChinese.append("/");
  31. suChinese.append(CNUMBERS[x]);
  32. }
  33. else
  34. {
  35. result = x + y;
  36. suChinese.append(CNUMBERS[x]);
  37. suChinese.append("+");
  38. suChinese.append(CNUMBERS[y]);
  39. }
  40. }
  41. else
  42. {
  43. if (x >= y)
  44. {
  45. result = x - y;
  46. suChinese.append(CNUMBERS[x]);
  47. suChinese.append("-");
  48. suChinese.append(CNUMBERS[y]);
  49. }
  50. else
  51. {
  52. result = y - x;
  53. suChinese.append(CNUMBERS[y]);
  54. suChinese.append("-");
  55. suChinese.append(CNUMBERS[x]);
  56. }
  57. }
  58. suChinese.append("=?@" + result);
  59. return suChinese.toString(); //5+6=?@11
  60. }
  61. }

2.得到表达式之后,我们需要对这个表达式进行分割,分成表达式和答案两部分

  1. // 这个表达式其实我们在工具类生成的时候做了处理,text其实是“1+2=@3”,这样就方便分离表达式和答案
  2. // 分割表达式
  3. expression = text.substring(0,text.lastIndexOf("@")) ; //"1+2=?"
  4. answer = text.substring(text.lastIndexOf("@")+1) ; //"@"

3.正常来说,接下来我们需要把数据存到缓存中去,但是因为普通类型的验证码也有这一步,所以这一部分重复逻辑就放在最后,我们现在需要根据前面的表达式部分来生成一张图片。

  1. // 最后,我们需要把“1+2=?”制作成一个图片返回给前端,用于展示给用户,
  2. // 制作成图片也有专门的工具类,然后我们需要把这个图片转成64编码返回给前端,这个功能代码其实是固定的
  3. //生成验证码图片(google.code.kaptcha提供的工具类Product)
  4. image = captchaProducerMath.createImage(expression);

   

captchaProducerMath是谷歌提供的工具类,我们只需要注入使用就行。

4.正常来说,现在应该是将验证码图片转成Base64编码,然后返回给前端展示,但是和上面缓存数据一样,两种编码类型都是获取到验证码数据缓存在redis并且把生成的验证码图片以Base64编码格式返回给前端,所以我们接下来就是为普通验证码类型获取验证码数据和生成图片。

④ 如果是普通类型的验证码

  1. else {
  2. /**
  3. * 如果不是计算式类型的验证码,那就是图形类型的验证码,那么就更简单了。
  4. * 只需要把图形验证码存到缓存中,然后再把图片返回给前端就好
  5. */
  6. // 图形验证码的话不能和上面一样生成表达式了,而是随机生成一个文本,然后把文本赋值给exception和answer,这样方便存储
  7. expression = answer= captchaProducer.createText();
  8. // 再把这个文本转成验证码图片
  9. image = captchaProducer.createImage(expression) ;
  10. }

captchaProducer和captchaProducerMath其实是一个类,知识取了不一样的名字方便区分。

⑤ 上面两种类型的验证码都已经成功得到表达式(exception),答案(anwser)(普通类型这个都一样),生成的图片(image)。

因为上面是哪一种类型就会生成哪一种验证码的数据,所以我们最后只需要生成一个uuid唯一标识作为key,然后把answer作为value存储在redis中。然后把image转换成Base64编码。

最后返回给前端image,uuid即可。

  1. // 然后把答案存入到redis中,当然了key不能直接用表达式,因为有可能会重复
  2. // 所以我们用uuid来作为key,然后把uuid给前端,前端在访问的时候再把uuid传来进行验证
  3. uuid = UUID.randomUUID().toString();; //uuid
  4. //Constants.CAPTCHA_EXPIRATION :表示2 , TimeUnit.MINUTES:表示分钟,即这个验证码数据只存储2分钟
  5. redisCache.setCacheObject(CacheConstants.CAPTCHA_CODE_KEY+uuid,answer,Constants.CAPTCHA_EXPIRATION , TimeUnit.MINUTES);
  6. //最后再把生成的验证码图片转成Base64编码
  7. //转之前需要先把图片转成字节数组
  8. FastByteArrayOutputStream os = new FastByteArrayOutputStream();
  9. ImageIO.write(image,"jpeg",os);//把图片通过jpeg类型写进字节数组
  10. //再转成Base64编码
  11. String base64Str = Base64.encode(os.toByteArray());
  12. //得到的Base64编码不能直接返回,还需要按照规定添加头部
  13. String base64Img = "data:image/jpeg;base64," +base64Str;
  14. //BASE64对密文进行传输加密时,可能出现\r\n
  15. //原因: RFC2045中有规定:即Base64一行不能超过76字符,超过则添加回车换行符。
  16. //解决方案: BASE64加密后,对\r\n进行去除
  17. base64Img= base64Img.replaceAll("\r|\n", "");
  18. // 最后把这个Base64编码的表达式图片验证码和用于表示key的uuid返回给前端
  19. ajaxResult.put("img",base64Img);
  20. ajaxResult.put("uuid",uuid) ;
  21. os.close();
  22. return ajaxResult ;

三 代码汇总

最后,我将完整的代码展出,并且包括了需要用到的工具类的代码

Controller层主要的逻辑部分代码:

  1. package com.fs.system.web.controller.common;
  2. import java.awt.image.BufferedImage;
  3. import java.io.IOException;
  4. import java.util.concurrent.TimeUnit;
  5. import javax.annotation.Resource;
  6. import javax.imageio.ImageIO;
  7. import javax.servlet.http.HttpServletResponse;
  8. import cn.hutool.core.codec.Base64;
  9. import cn.hutool.core.lang.UUID;
  10. import com.fs.common.config.FeiSiConfig;
  11. import com.fs.common.constant.CacheConstants;
  12. import com.fs.common.constant.Constants;
  13. import com.fs.common.core.vo.AjaxResult;
  14. import com.fs.common.util.RedisCache;
  15. import com.fs.system.service.ISysConfigService;
  16. import com.fs.system.util.KaptchaTextCreator;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.util.FastByteArrayOutputStream;
  19. import org.springframework.web.bind.annotation.GetMapping;
  20. import org.springframework.web.bind.annotation.RestController;
  21. import com.google.code.kaptcha.Producer;
  22. /**
  23. * 验证码操作处理
  24. *
  25. */
  26. @RestController
  27. public class CaptchaController{
  28. @Autowired
  29. private ISysConfigService configService ; //判断是否开启验证码
  30. @Autowired
  31. private FeiSiConfig feiSiConfig ; //配置信息
  32. @Autowired
  33. private KaptchaTextCreator kaptchaTextCreator ; //随机生成验证码表达式
  34. @Autowired
  35. private RedisCache redisCache ; //存储数据到缓存
  36. @Resource(name = "captchaProducer")
  37. private Producer captchaProducer; //生成普通类型验证码图片
  38. @Resource(name="captchaProducerMath")
  39. private Producer captchaProducerMath; //生成计算类型验证码图片
  40. /**
  41. * 生成验证码
  42. */
  43. @GetMapping("/captchaImage")
  44. public AjaxResult getCode(HttpServletResponse response) throws IOException
  45. {
  46. AjaxResult ajaxResult = AjaxResult.success() ;
  47. /**
  48. * 思路:
  49. * 我们目前验证分为两种,一种是计算类型验证码,一种是单纯的图形验证码
  50. * 所以,我们第一步就是判断验证码的类型,而验证码的类型是我们在配置文件配置好的
  51. */
  52. // 先要判断一下是否开启了验证码功能,如果没有开启,则不需要进行生成验证码的操作了
  53. boolean captchaEnabled = configService.selectCaptchaEnabled();
  54. if (!captchaEnabled){
  55. return ajaxResult.put("captchaOnOff",captchaEnabled) ; //返回没有开启验证码信息给前端
  56. }
  57. BufferedImage image = null ;//图片
  58. String expression ; //表达式部分
  59. String answer ; //答案部分
  60. String uuid ;; //uuid
  61. // 否则说明开启了验证码,那么需要判断使用的是什么类型的验证码
  62. /**
  63. * 至于这个captchaType的值是根据配置文件设置的,因为这个FeisiConfig是一个配置类
  64. * 它加了‘@ConfigurationProperties(prefix = "fs")’这个注解,
  65. * 而配置文件规定math是计算类型验证码,char是图形类型验证码
  66. */
  67. String captchaType = feiSiConfig.getCaptchaType();
  68. if (captchaType.equals("math")){
  69. // 如果验证码的类型是一个计算类型验证码
  70. /**
  71. * 那么我们就需要分析一下:
  72. * 比如一个计算类型的验证码:1+2=?
  73. * 那么我们到时候应该填入的验证码的答案是:3
  74. * 所以这个验证码它包含了两个部分,一个是"1+2=?" 另外一个是"3"
  75. * 其实生成这个计算问题是我们自己来实现的,我们的工具类已经封装好了计算提,比如:“1+2=3”
  76. * 所以我们现在要做的主要有三步:
  77. * 1.得到这个算式后,将算是“1+2=3”分割成两个部分,“1+2=?” 和 “3”
  78. * 2.然后我们需要把它们存储到缓存中去,前面可以当作key,后面可以当作value,然后进行验证答案的正确性
  79. * 3.最后,我们需要把“1+2=?”制作成一个图片返回给前端,用于展示给用户,
  80. */
  81. // 获取一个随机生成的表达式(随机生成的工具封装在KaptchaTextCreator类中)
  82. String text = kaptchaTextCreator.getText();
  83. // 这个表达式其实我们在工具类生成的时候做了处理,text其实是“1+2=@3”,这样就方便分离表达式和答案
  84. // 分割表达式
  85. expression = text.substring(0,text.lastIndexOf("@")) ; //"1+2=?"
  86. answer = text.substring(text.lastIndexOf("@")+1) ; //"@"
  87. // 最后,我们需要把“1+2=?”制作成一个图片返回给前端,用于展示给用户,
  88. // 制作成图片也有专门的工具类,然后我们需要把这个图片转成64编码返回给前端,这个功能代码其实是固定的
  89. //生成验证码图片(google.code.kaptcha提供的工具类Product)
  90. image = captchaProducerMath.createImage(expression);
  91. }else {
  92. /**
  93. * 如果不是计算式类型的验证码,那就是图形类型的验证码,那么就更简单了。
  94. * 只需要把图形验证码存到缓存中,然后再把图片返回给前端就好
  95. */
  96. // 图形验证码的话不能和上面一样生成表达式了,而是随机生成一个文本,然后把文本赋值给exception和answer,这样方便存储
  97. expression = answer= captchaProducer.createText();
  98. // 再把这个文本转成验证码图片
  99. image = captchaProducer.createImage(expression) ;
  100. }
  101. System.out.println(expression+":"+answer);
  102. // 然后把答案存入到redis中,当然了key不能直接用表达式,因为有可能会重复
  103. // 所以我们用uuid来作为key,然后把uuid给前端,前端在访问的时候再把uuid传来进行验证
  104. uuid = UUID.randomUUID().toString();; //uuid
  105. //Constants.CAPTCHA_EXPIRATION :表示2 , TimeUnit.MINUTES:表示分钟,即这个验证码数据只存储2分钟
  106. redisCache.setCacheObject(CacheConstants.CAPTCHA_CODE_KEY+uuid,answer,Constants.CAPTCHA_EXPIRATION , TimeUnit.MINUTES);
  107. //最后再把生成的验证码图片转成Base64编码
  108. //转之前需要先把图片转成字节数组
  109. FastByteArrayOutputStream os = new FastByteArrayOutputStream();
  110. ImageIO.write(image,"jpeg",os);//把图片通过jpeg类型写进字节数组
  111. //再转成Base64编码
  112. String base64Str = Base64.encode(os.toByteArray());
  113. //得到的Base64编码不能直接返回,还需要按照规定添加头部
  114. String base64Img = "data:image/jpeg;base64," +base64Str;
  115. //BASE64对密文进行传输加密时,可能出现\r\n
  116. //原因: RFC2045中有规定:即Base64一行不能超过76字符,超过则添加回车换行符。
  117. //解决方案: BASE64加密后,对\r\n进行去除
  118. base64Img= base64Img.replaceAll("\r|\n", "");
  119. // 最后把这个Base64编码的表达式图片验证码和用于表示key的uuid返回给前端
  120. ajaxResult.put("img",base64Img);
  121. ajaxResult.put("uuid",uuid) ;
  122. os.close();
  123. return ajaxResult ;
  124. }
  125. }

 用于判断是否开启验证码的功能的configService类代码:

  1. package com.fs.system.service.ipml;
  2. import cn.hutool.core.convert.Convert;
  3. import cn.hutool.core.util.StrUtil;
  4. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  5. import com.fs.common.constant.CacheConstants;
  6. import com.fs.common.core.pojo.SysConfig;
  7. import com.fs.common.util.RedisCache;
  8. import com.fs.common.util.StringUtils;
  9. import com.fs.system.mapper.SysConfigMapper;
  10. import com.fs.system.service.ISysConfigService;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import javax.annotation.PostConstruct;
  14. import java.util.List;
  15. import java.util.Objects;
  16. @Service
  17. public class SysConfigServiceImpl implements ISysConfigService {
  18. @Autowired
  19. private SysConfigMapper sysConfigMapper;
  20. @Autowired
  21. private RedisCache redisCache;
  22. @PostConstruct //标记在方法上, 当这个bean创建完成,自动执行这个注解标记方法,要求这个方法无参 类似 init-method配置
  23. public void init(){
  24. loadingConfigCache();
  25. }
  26. // 初始化数据,当系统加载的时候,把数据存入到缓存中去
  27. @Override
  28. public void loadingConfigCache() {
  29. System.out.println("初始化数据...");
  30. //查询数据库
  31. List<SysConfig> sysConfigs = sysConfigMapper.selectList(null);
  32. //保存到redis缓存中
  33. sysConfigs.forEach((sysConfig)->{
  34. redisCache.setCacheObject(getCacheKey(sysConfig.getConfigKey()),sysConfig.getConfigValue());
  35. });
  36. }
  37. /**
  38. * 构建参数配置表的redis的key
  39. * @param configKey
  40. * @return
  41. */
  42. // 获取redis缓存中的key
  43. private String getCacheKey(String configKey){
  44. return CacheConstants.SYS_CONFIG_KEY+configKey;
  45. }
  46. // 判断账号的验证码是否以及开启
  47. @Override
  48. public boolean selectCaptchaEnabled() {
  49. String cacheObject = redisCache.getCacheObject("sys.account.captchaOnOff");
  50. if (StrUtil.isBlank(cacheObject)){
  51. // 如果查询出来的是空,则说明该账号是第一次登录,则默认开启验证码
  52. return true ;
  53. }
  54. // 如果不是空,那么查询到的数据不是true就是false,但是存到数据库的并不是Boolean类型而是String类型,
  55. // 所以我们借用工具直接把字符串转成对应的Boolean类型
  56. return Convert.toBool(cacheObject) ;
  57. }
  58. // 根据账号的key获取缓存中的value
  59. @Override
  60. public String selectConfigByKey(String configKey) {
  61. // 1. 如果从redis中得到了数据,那么直接返回
  62. String cacheObject = redisCache.getCacheObject(getCacheKey(configKey));
  63. // 我们需要返回的是一个String类型,所以需要用工具包转为String类型
  64. String toStr = Convert.toStr(cacheObject);
  65. if (StrUtil.isNotBlank(toStr)){
  66. return toStr ;
  67. }
  68. // 2.如果没有得到数据,那么我们需要从sys_config数据库表中查询数据,同时把查询到的数据存入缓存中
  69. LambdaQueryWrapper<SysConfig> queryWrapper=new LambdaQueryWrapper<>();
  70. queryWrapper.eq(SysConfig::getConfigKey,configKey);
  71. SysConfig sysConfig = sysConfigMapper.selectOne(queryWrapper);
  72. // 如果查到有数据,就存入redis并且返回
  73. if(Objects.nonNull(sysConfig)){ //这里判空的方法不能继续和上面一样,因为sysConfig并不是字符串类型,而是一个对象
  74. // 获取到值
  75. String configValue = sysConfig.getConfigValue();
  76. redisCache.setCacheObject(getCacheKey(configKey),configValue);
  77. return configValue;
  78. }
  79. // 否则没有查到数据,则说明没有信息
  80. return null ;
  81. }
  82. }

验证码类型配置信息的配置类feiSiConfig , 以及配置文件yam的代码

  1. package com.fs.common.config;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * 读取项目相关配置
  6. *
  7. */
  8. @Component
  9. @ConfigurationProperties(prefix = "fs")
  10. public class FeiSiConfig
  11. {
  12. /** 项目名称 */
  13. private String name;
  14. /** 版本 */
  15. private String version;
  16. /** 版权年份 */
  17. private String copyrightYear;
  18. /** 上传路径 */
  19. private static String profile;
  20. /** 获取地址开关 */
  21. private static boolean addressEnabled;
  22. /** 验证码类型 */
  23. private static String captchaType;
  24. public String getName()
  25. {
  26. return name;
  27. }
  28. public void setName(String name)
  29. {
  30. this.name = name;
  31. }
  32. public String getVersion()
  33. {
  34. return version;
  35. }
  36. public void setVersion(String version)
  37. {
  38. this.version = version;
  39. }
  40. public String getCopyrightYear()
  41. {
  42. return copyrightYear;
  43. }
  44. public void setCopyrightYear(String copyrightYear)
  45. {
  46. this.copyrightYear = copyrightYear;
  47. }
  48. public static String getProfile()
  49. {
  50. return profile;
  51. }
  52. public void setProfile(String profile)
  53. {
  54. FeiSiConfig.profile = profile;
  55. }
  56. public static boolean isAddressEnabled()
  57. {
  58. return addressEnabled;
  59. }
  60. public void setAddressEnabled(boolean addressEnabled)
  61. {
  62. FeiSiConfig.addressEnabled = addressEnabled;
  63. }
  64. public static String getCaptchaType() {
  65. return captchaType;
  66. }
  67. public void setCaptchaType(String captchaType) {
  68. FeiSiConfig.captchaType = captchaType;
  69. }
  70. /**
  71. * 获取导入上传路径
  72. */
  73. public static String getImportPath()
  74. {
  75. return getProfile() + "/import";
  76. }
  77. /**
  78. * 获取头像上传路径
  79. */
  80. public static String getAvatarPath()
  81. {
  82. return getProfile() + "/avatar";
  83. }
  84. /**
  85. * 获取下载路径
  86. */
  87. public static String getDownloadPath()
  88. {
  89. return getProfile() + "/download/";
  90. }
  91. /**
  92. * 获取上传路径
  93. */
  94. public static String getUploadPath()
  95. {
  96. return getProfile() + "/upload";
  97. }
  98. }

 yml配置文件:

  1. # 项目相关配置
  2. fs:
  3. # 名称
  4. name: FeiSi
  5. # 版本
  6. version: 1.0.0
  7. # 版权年份
  8. copyrightYear: 2023
  9. # 文件路径 示例( Windows配置D:/feisi/uploadPath,Linux配置 /home/feisi/uploadPath)
  10. profile: D:/feisi/uploadPath
  11. # 获取ip地址开关
  12. addressEnabled: false
  13. # 验证码类型 math 数字计算 char 字符验证
  14. captchaType: math

生成随机计算类型表达式的 kaptchaTextCreator类代码

  1. package com.fs.system.util;
  2. import com.google.code.kaptcha.text.impl.DefaultTextCreator;
  3. import org.springframework.stereotype.Component;
  4. import java.util.Random;
  5. @Component
  6. public class KaptchaTextCreator extends DefaultTextCreator {
  7. private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
  8. @Override
  9. public String getText()
  10. {
  11. Integer result = 0;
  12. Random random = new Random();
  13. int x = random.nextInt(10);
  14. int y = random.nextInt(10);
  15. StringBuilder suChinese = new StringBuilder();
  16. int randomoperands = random.nextInt(3);
  17. if (randomoperands == 0)
  18. {
  19. result = x * y;
  20. suChinese.append(CNUMBERS[x]);
  21. suChinese.append("*");
  22. suChinese.append(CNUMBERS[y]);
  23. }
  24. else if (randomoperands == 1)
  25. {
  26. if ((x != 0) && y % x == 0)
  27. {
  28. result = y / x;
  29. suChinese.append(CNUMBERS[y]);
  30. suChinese.append("/");
  31. suChinese.append(CNUMBERS[x]);
  32. }
  33. else
  34. {
  35. result = x + y;
  36. suChinese.append(CNUMBERS[x]);
  37. suChinese.append("+");
  38. suChinese.append(CNUMBERS[y]);
  39. }
  40. }
  41. else
  42. {
  43. if (x >= y)
  44. {
  45. result = x - y;
  46. suChinese.append(CNUMBERS[x]);
  47. suChinese.append("-");
  48. suChinese.append(CNUMBERS[y]);
  49. }
  50. else
  51. {
  52. result = y - x;
  53. suChinese.append(CNUMBERS[y]);
  54. suChinese.append("-");
  55. suChinese.append(CNUMBERS[x]);
  56. }
  57. }
  58. suChinese.append("=?@" + result);
  59. return suChinese.toString(); //5+6=?@11
  60. }
  61. }

封装redis,把数据存储在redis缓存的工具类的redisCache类的代码:

  1. package com.fs.common.util;
  2. import java.util.Collection;
  3. import java.util.Iterator;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Set;
  7. import java.util.concurrent.TimeUnit;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.data.redis.core.BoundSetOperations;
  10. import org.springframework.data.redis.core.HashOperations;
  11. import org.springframework.data.redis.core.RedisTemplate;
  12. import org.springframework.data.redis.core.ValueOperations;
  13. import org.springframework.stereotype.Component;
  14. /**
  15. * spring redis 工具类
  16. *
  17. **/
  18. @SuppressWarnings(value = { "unchecked", "rawtypes" })
  19. @Component
  20. public class RedisCache
  21. {
  22. @Autowired
  23. public RedisTemplate redisTemplate;
  24. /**
  25. * 缓存基本的对象,Integer、String、实体类等
  26. *
  27. * @param key 缓存的键值
  28. * @param value 缓存的值
  29. */
  30. public <T> void setCacheObject(final String key, final T value)
  31. {
  32. redisTemplate.opsForValue().set(key, value);
  33. }
  34. /**
  35. * 缓存基本的对象,Integer、String、实体类等
  36. *
  37. * @param key 缓存的键值
  38. * @param value 缓存的值
  39. * @param timeout 时间
  40. * @param timeUnit 时间颗粒度
  41. */
  42. public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
  43. {
  44. redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
  45. }
  46. /**
  47. * 设置有效时间
  48. *
  49. * @param key Redis键
  50. * @param timeout 超时时间
  51. * @return true=设置成功;false=设置失败
  52. */
  53. public boolean expire(final String key, final long timeout)
  54. {
  55. return expire(key, timeout, TimeUnit.SECONDS);
  56. }
  57. /**
  58. * 设置有效时间
  59. *
  60. * @param key Redis键
  61. * @param timeout 超时时间
  62. * @param unit 时间单位
  63. * @return true=设置成功;false=设置失败
  64. */
  65. public boolean expire(final String key, final long timeout, final TimeUnit unit)
  66. {
  67. return redisTemplate.expire(key, timeout, unit);
  68. }
  69. /**
  70. * 获取有效时间
  71. *
  72. * @param key Redis键
  73. * @return 有效时间
  74. */
  75. public long getExpire(final String key)
  76. {
  77. return redisTemplate.getExpire(key);
  78. }
  79. /**
  80. * 判断 key是否存在
  81. *
  82. * @param key 键
  83. * @return true 存在 false不存在
  84. */
  85. public Boolean hasKey(String key)
  86. {
  87. return redisTemplate.hasKey(key);
  88. }
  89. /**
  90. * 获得缓存的基本对象。
  91. *
  92. * @param key 缓存键值
  93. * @return 缓存键值对应的数据
  94. */
  95. public <T> T getCacheObject(final String key)
  96. {
  97. ValueOperations<String, T> operation = redisTemplate.opsForValue();
  98. return operation.get(key);
  99. }
  100. /**
  101. * 删除单个对象
  102. *
  103. * @param key
  104. */
  105. public boolean deleteObject(final String key)
  106. {
  107. return redisTemplate.delete(key);
  108. }
  109. /**
  110. * 删除集合对象
  111. *
  112. * @param collection 多个对象
  113. * @return
  114. */
  115. public boolean deleteObject(final Collection collection)
  116. {
  117. return redisTemplate.delete(collection) > 0;
  118. }
  119. /**
  120. * 缓存List数据
  121. *
  122. * @param key 缓存的键值
  123. * @param dataList 待缓存的List数据
  124. * @return 缓存的对象
  125. */
  126. public <T> long setCacheList(final String key, final List<T> dataList)
  127. {
  128. Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
  129. return count == null ? 0 : count;
  130. }
  131. /**
  132. * 获得缓存的list对象
  133. *
  134. * @param key 缓存的键值
  135. * @return 缓存键值对应的数据
  136. */
  137. public <T> List<T> getCacheList(final String key)
  138. {
  139. return redisTemplate.opsForList().range(key, 0, -1);
  140. }
  141. /**
  142. * 缓存Set
  143. *
  144. * @param key 缓存键值
  145. * @param dataSet 缓存的数据
  146. * @return 缓存数据的对象
  147. */
  148. public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
  149. {
  150. BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
  151. Iterator<T> it = dataSet.iterator();
  152. while (it.hasNext())
  153. {
  154. setOperation.add(it.next());
  155. }
  156. return setOperation;
  157. }
  158. /**
  159. * 获得缓存的set
  160. *
  161. * @param key
  162. * @return
  163. */
  164. public <T> Set<T> getCacheSet(final String key)
  165. {
  166. return redisTemplate.opsForSet().members(key);
  167. }
  168. /**
  169. * 缓存Map
  170. *
  171. * @param key
  172. * @param dataMap
  173. */
  174. public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
  175. {
  176. if (dataMap != null) {
  177. redisTemplate.opsForHash().putAll(key, dataMap);
  178. }
  179. }
  180. /**
  181. * 获得缓存的Map
  182. *
  183. * @param key
  184. * @return
  185. */
  186. public <T> Map<String, T> getCacheMap(final String key)
  187. {
  188. return redisTemplate.opsForHash().entries(key);
  189. }
  190. /**
  191. * 往Hash中存入数据
  192. *
  193. * @param key Redis键
  194. * @param hKey Hash键
  195. * @param value 值
  196. */
  197. public <T> void setCacheMapValue(final String key, final String hKey, final T value)
  198. {
  199. redisTemplate.opsForHash().put(key, hKey, value);
  200. }
  201. /**
  202. * 获取Hash中的数据
  203. *
  204. * @param key Redis键
  205. * @param hKey Hash键
  206. * @return Hash中的对象
  207. */
  208. public <T> T getCacheMapValue(final String key, final String hKey)
  209. {
  210. HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
  211. return opsForHash.get(key, hKey);
  212. }
  213. /**
  214. * 获取多个Hash中的数据
  215. *
  216. * @param key Redis键
  217. * @param hKeys Hash键集合
  218. * @return Hash对象集合
  219. */
  220. public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
  221. {
  222. return redisTemplate.opsForHash().multiGet(key, hKeys);
  223. }
  224. /**
  225. * 删除Hash中的某条数据
  226. *
  227. * @param key Redis键
  228. * @param hKey Hash键
  229. * @return 是否成功
  230. */
  231. public boolean deleteCacheMapValue(final String key, final String hKey)
  232. {
  233. return redisTemplate.opsForHash().delete(key, hKey) > 0;
  234. }
  235. /**
  236. * 获得缓存的基本对象列表
  237. *
  238. * @param pattern 字符串前缀
  239. * @return 对象列表
  240. */
  241. public Collection<String> keys(final String pattern)
  242. {
  243. return redisTemplate.keys(pattern);
  244. }
  245. }

以上就是实现验证码功能的整个后端代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/987790
推荐阅读
相关标签
  

闽ICP备14008679号