SpringBoot进阶教程(八十三)Kaptcha

打印 上一主题 下一主题

主题 928|帖子 928|积分 2784

Kaptcha是谷歌开源的一个可高度配置的比较老旧的实用验证码天生工具。它可以实现:(1)验证码的字体/大小颜色;(2)验证码内容的范围(数字,字母,中文汉字);(3)验证码图片的大小,边框,边框粗细,边框颜色(4)验证码的干扰线验证码的样式(鱼眼样式、3D、 普通暗昧)。
v搭建架构

添加maven引用
  1.         <dependency>
  2.             <groupId>com.github.penggle</groupId>
  3.             <artifactId>kaptcha</artifactId>
  4.             <version>2.3.2</version>
  5.         </dependency>
复制代码
创建Kaptcha配置类
  1. /**
  2. * @Author chen bo
  3. * @Date 2023/12
  4. * @Des
  5. */
  6. @Configuration
  7. public class KaptchaConfig {
  8.     @Bean
  9.     public DefaultKaptcha getDefaultKaptcha() {
  10.         com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
  11.         Properties properties = new Properties();
  12.         properties.put("kaptcha.border", "no");
  13.         properties.put("kaptcha.textproducer.font.color", "black");
  14.         properties.put("kaptcha.image.width", "200");
  15.         properties.put("kaptcha.image.height", "50");
  16.         properties.put("kaptcha.textproducer.font.size", "25");
  17.         properties.put("kaptcha.session.key", "verifyCode");
  18.         properties.put("kaptcha.textproducer.char.space", "5");
  19.         Config config = new Config(properties);
  20.         defaultKaptcha.setConfig(config);
  21.         return defaultKaptcha;
  22.     }
  23. }
复制代码
此处配置的类可参考下方的配置表格:
常量描述默认值kaptcha.border图片边框,合法值:yes , noyeskaptcha.border.color边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.blackkaptcha.border.thickness边框厚度,合法值:>01kaptcha.image.width图片宽200kaptcha.image.height图片高50kaptcha.producer.impl图片实现类com.google.code.kaptcha.impl.DefaultKaptchakaptcha.textproducer.font.size文本实现类com.google.code.kaptcha.text.impl.DefaultTextCreatorkaptcha.textproducer.font.size字体大小40px.kaptcha.textproducer.font.color字体颜色,合法值: r,g,b 或者 white,black,blue.blackkaptcha.textproducer.char.space文字间隔2kaptcha.noise.impl干扰实现类com.google.code.kaptcha.impl.DefaultNoisekaptcha.noise.color干扰 颜色,合法值: r,g,b 或者 white,black,blue.blackkaptcha.obscurificator.impl图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpycom.google.code.kaptcha.impl.WaterRipplekaptcha.background.impl背景实现类com.google.code.kaptcha.impl.DefaultBackgroundkaptcha.background.clear.from背景颜色渐变,开始颜色light greykaptcha.background.clear.to背景颜色渐变, 结束颜色whitekaptcha.textproducer.char.length验证码长度5 创建controller
  1. /**
  2. * @Author chen bo
  3. * @Date 2023/12
  4. * @Des
  5. */
  6. @RestController
  7. @RequestMapping("/demo")
  8. @Slf4j
  9. public class ImageController {
  10.     @Autowired
  11.     private DefaultKaptcha defaultKaptcha;
  12.     @RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
  13.     public void getKaptcha(HttpServletResponse response, HttpSession session) {
  14.         String text = defaultKaptcha.createText();
  15.         BufferedImage image = defaultKaptcha.createImage(text);
  16.         // 线上环境这个验证码肯定是要存redis里的,redis的key还需要设置一个合理的过期时间
  17.         session.setAttribute("kaptcha", text);
  18.         response.setContentType("image/png");
  19.         try {
  20.             ServletOutputStream os = response.getOutputStream();
  21.             ImageIO.write(image, "png", os);
  22.         } catch (IOException e) {
  23.             log.error("响应验证码失败:" + e.getMessage());
  24.         }
  25.     }
  26.     @CrossOrigin
  27.     @RequestMapping(path = "/login", method = RequestMethod.POST)
  28.     public String login(HttpSession session,  String kaptcha) {
  29.         if(kaptcha.equals(session.getAttribute("kaptcha"))){
  30.             return kaptcha + "验证码正确";
  31.         }else{
  32.             return kaptcha + "验证码错误";
  33.         }
  34.     }
  35. }
复制代码
添加登录页面
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>Title</title>
  6.    
  7. </head>
  8. <body>
  9. <h3>请登录</h3>
  10.     <input type="text" placeholder="请输入用户名" name="username" required="required"/>
  11.     <br/>
  12.     <input type="password" placeholder="请输入密码" name="password" required="required"/>
  13.     <br/>
  14.     <span style="display: inline">
  15.             <input type="text" name="请输入验证码" id="kaptcha_value" placeholder="验证码" required="required"/>
  16.             <img src="http://localhost:8301/demo/kaptcha" id="kaptcha" style="width:100px;height:50px;" class="mr-2"/>
  17.             <a target="_blank" href="https://www.cnblogs.com/javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
  18.     </span>
  19.     <br/>
  20.     <button type="submit" onclick="login()">登录</button>
  21. </body>
  22. </html>
复制代码
效果图

v自定义验证码文本天生器

创建自定义文本天生器
  1. /**
  2. * @Author chen bo
  3. * @Date 2023/12
  4. * @Des
  5. */
  6. public class CustomTextCreator extends DefaultTextCreator {
  7.     private static final String[] Number = "0,1,2,3,4,5,6,7,8,9,10".split(",");
  8.     @Override
  9.     public String getText()
  10.     {
  11.         int result;
  12.         Random random = new Random();
  13.         int x = random.nextInt(10);
  14.         int y = random.nextInt(10);
  15.         StringBuilder suChinese = new StringBuilder();
  16.         int randomOperand = (int) Math.round(Math.random() * 2);
  17.         if (randomOperand == 0) {
  18.             result = x * y;
  19.             suChinese.append(Number[x]);
  20.             suChinese.append("*");
  21.             suChinese.append(Number[y]);
  22.         } else if (randomOperand == 1) {
  23.             if (!(x == 0) && y % x == 0) {
  24.                 result = y / x;
  25.                 suChinese.append(Number[y]);
  26.                 suChinese.append("/");
  27.                 suChinese.append(Number[x]);
  28.             } else {
  29.                 result = x + y;
  30.                 suChinese.append(Number[x]);
  31.                 suChinese.append("+");
  32.                 suChinese.append(Number[y]);
  33.             }
  34.         } else if (randomOperand == 2) {
  35.             if (x >= y) {
  36.                 result = x - y;
  37.                 suChinese.append(Number[x]);
  38.                 suChinese.append("-");
  39.                 suChinese.append(Number[y]);
  40.             } else {
  41.                 result = y - x;
  42.                 suChinese.append(Number[y]);
  43.                 suChinese.append("-");
  44.                 suChinese.append(Number[x]);
  45.             }
  46.         } else {
  47.             result = x + y;
  48.             suChinese.append(Number[x]);
  49.             suChinese.append("+");
  50.             suChinese.append(Number[y]);
  51.         }
  52.         suChinese.append("=?@").append(result);
  53.         return suChinese.toString();
  54.     }
  55. }
复制代码
更新Kaptcha配置类
  1. /**
  2. * @Author chen bo
  3. * @Date 2023/12
  4. * @Des
  5. */
  6. @Configuration
  7. public class KaptchaConfig {
  8.     //    @Bean
  9. //    public DefaultKaptcha getDefaultKaptcha() {
  10. //        com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
  11. //        Properties properties = new Properties();
  12. //        properties.put("kaptcha.border", "no");
  13. //        properties.put("kaptcha.textproducer.font.color", "black");
  14. //        properties.put("kaptcha.image.width", "200");
  15. //        properties.put("kaptcha.image.height", "50");
  16. //        properties.put("kaptcha.textproducer.font.size", "25");
  17. //        properties.put("kaptcha.session.key", "verifyCode");
  18. //        properties.put("kaptcha.textproducer.char.space", "5");
  19. //        Config config = new Config(properties);
  20. //        defaultKaptcha.setConfig(config);
  21. //
  22. //        return defaultKaptcha;
  23. //    }
  24.     @Bean(name = "captchaProducerMath")
  25.     public DefaultKaptcha getKaptchaBeanMath() {
  26.         DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
  27.         Properties properties = new Properties();
  28.         // 是否有边框 默认为true 我们可以自己设置yes,no
  29.         properties.setProperty("kaptcha.border", "yes");
  30.         // 边框颜色 默认为Color.BLACK
  31.         properties.setProperty("kaptcha.border.color", "105,179,90");
  32.         // 验证码文本字符颜色 默认为Color.BLACK
  33.         properties.setProperty("kaptcha.textproducer.font.color", "blue");
  34.         // 验证码图片宽度 默认为200
  35.         properties.setProperty("kaptcha.image.width", "160");
  36.         // 验证码图片高度 默认为50
  37.         properties.setProperty("kaptcha.image.height", "60");
  38.         // 验证码文本字符大小 默认为40
  39.         properties.setProperty("kaptcha.textproducer.font.size", "35");
  40.         // KAPTCHA_SESSION_KEY
  41.         properties.setProperty("kaptcha.session.key", "kaptchaCodeMath");
  42.         // 验证码文本生成器
  43.         properties.setProperty("kaptcha.textproducer.impl", "com.kaptcha.demo.util.CustomTextCreator");
  44.         // 验证码文本字符间距 默认为2
  45.         properties.setProperty("kaptcha.textproducer.char.space", "3");
  46.         // 验证码文本字符长度 默认为5
  47.         properties.setProperty("kaptcha.textproducer.char.length", "6");
  48.         // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1,
  49.         // fontSize)
  50.         properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
  51.         // 验证码噪点颜色 默认为Color.BLACK
  52.         properties.setProperty("kaptcha.noise.color", "white");
  53.         // 干扰实现类
  54.         properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
  55.         // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple
  56.         // 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
  57.         // 阴影com.google.code.kaptcha.impl.ShadowGimpy
  58.         properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
  59.         Config config = new Config(properties);
  60.         defaultKaptcha.setConfig(config);
  61.         return defaultKaptcha;
  62.     }
  63. }
复制代码
创建CustomController
  1. /**
  2. * @Author chen bo
  3. * @Date 2023/12
  4. * @Des
  5. */
  6. @RestController
  7. @Slf4j
  8. @RequestMapping("/custom")
  9. public class CustomController {
  10.     @Autowired
  11.     private Producer producer;
  12.     public static final String DEFAULT_CODE_KEY = "random_code_";
  13.     /**
  14.      * @MethodName createCaptcha
  15.      * @Description  生成验证码
  16.      * @param httpServletResponse 响应流
  17.      * @Author hl
  18.      * @Date 2022/12/6 10:30
  19.      */
  20.     @GetMapping("/create")
  21.     public void createCaptcha(HttpServletResponse httpServletResponse, HttpSession session) throws IOException {
  22.         // 生成验证码
  23.         String capText = producer.createText();
  24.         String capStr = capText.substring(0, capText.lastIndexOf("@"));
  25.         String result = capText.substring(capText.lastIndexOf("@") + 1);
  26.         BufferedImage image = producer.createImage(capStr);
  27.         // 保存验证码信息
  28.         String randomStr = UUID.randomUUID().toString().replaceAll("-", "");
  29.         System.out.println("随机数为:" + randomStr);
  30.         //redisTemplate.opsForValue().set(DEFAULT_CODE_KEY + randomStr, result, 3600, TimeUnit.SECONDS);
  31.         session.setAttribute("kaptcha", result);
  32.         // 转换流信息写出
  33.         FastByteArrayOutputStream os = new FastByteArrayOutputStream();
  34.         try {
  35.             ImageIO.write(image, "jpg", os);
  36.         } catch (IOException e) {
  37.             log.error("ImageIO write err", e);
  38.             httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
  39.             return;
  40.         }
  41.         // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
  42.         byte[] bytes = os.toByteArray();
  43.         //设置响应头
  44.         httpServletResponse.setHeader("Cache-Control", "no-store");
  45.         //设置响应头
  46.         httpServletResponse.setHeader("randomstr",randomStr);
  47.         //设置响应头
  48.         httpServletResponse.setHeader("Pragma", "no-cache");
  49.         //在代理服务器端防止缓冲
  50.         httpServletResponse.setDateHeader("Expires", 0);
  51.         //设置响应内容类型
  52.         ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
  53.         responseOutputStream.write(bytes);
  54.         responseOutputStream.flush();
  55.         responseOutputStream.close();
  56.     }
  57. }
复制代码
效果图


v前后端验证码实现流程扩展


  • 前端向后端请求验证码。
  • 后端通过谷歌开源工具Kaptcha天生图形验证码(实际是5个随机字符),缓存到redis,key键可以是 业务+用户id,value值就是那5个随机字符。设置TTL为2分钟。
  • 后端将图形验证码转base64编码字符串,将该字符串返回给前端。
  • 前端解析base64编码的字符串,即可在页面上显示图形验证码。
  • 用户输入密码与验证码后提交表单到后端。
  • 后端根据业务和用户id组成的key键到redis查找缓存的验证码信息,会有如下环境:

    • 如果redis返回为空,则通知前端验证码失效,需要重新获取验证码。
    • 如果redis返回不为空,但是不相当,说明验证码输入错误。则删除redis中对应验证码缓存,通知前端验证码错误,需要重新获取验证码。
    • 如果redis返回不为空,并且相当,则校验成功,就删除redis中对应验证码缓存,并在mysql中修改密码。最后通知前端修改成功。

其他参考/学习资料:
v源码地址

https://github.com/toutouge/javademosecond/tree/master/kaptcha-demo

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

汕尾海湾

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表