Kaptcha是谷歌开源的一个可高度配置的比较老旧的实用验证码天生工具。它可以实现:(1)验证码的字体/大小颜色;(2)验证码内容的范围(数字,字母,中文汉字);(3)验证码图片的大小,边框,边框粗细,边框颜色(4)验证码的干扰线验证码的样式(鱼眼样式、3D、 普通暗昧)。
v搭建架构
添加maven引用- <dependency>
- <groupId>com.github.penggle</groupId>
- <artifactId>kaptcha</artifactId>
- <version>2.3.2</version>
- </dependency>
复制代码 创建Kaptcha配置类- /**
- * @Author chen bo
- * @Date 2023/12
- * @Des
- */
- @Configuration
- public class KaptchaConfig {
- @Bean
- public DefaultKaptcha getDefaultKaptcha() {
- com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
- Properties properties = new Properties();
- properties.put("kaptcha.border", "no");
- properties.put("kaptcha.textproducer.font.color", "black");
- properties.put("kaptcha.image.width", "200");
- properties.put("kaptcha.image.height", "50");
- properties.put("kaptcha.textproducer.font.size", "25");
- properties.put("kaptcha.session.key", "verifyCode");
- properties.put("kaptcha.textproducer.char.space", "5");
- Config config = new Config(properties);
- defaultKaptcha.setConfig(config);
- return defaultKaptcha;
- }
- }
复制代码 此处配置的类可参考下方的配置表格:
常量描述默认值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- /**
- * @Author chen bo
- * @Date 2023/12
- * @Des
- */
- @RestController
- @RequestMapping("/demo")
- @Slf4j
- public class ImageController {
- @Autowired
- private DefaultKaptcha defaultKaptcha;
- @RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
- public void getKaptcha(HttpServletResponse response, HttpSession session) {
- String text = defaultKaptcha.createText();
- BufferedImage image = defaultKaptcha.createImage(text);
- // 线上环境这个验证码肯定是要存redis里的,redis的key还需要设置一个合理的过期时间
- session.setAttribute("kaptcha", text);
- response.setContentType("image/png");
- try {
- ServletOutputStream os = response.getOutputStream();
- ImageIO.write(image, "png", os);
- } catch (IOException e) {
- log.error("响应验证码失败:" + e.getMessage());
- }
- }
- @CrossOrigin
- @RequestMapping(path = "/login", method = RequestMethod.POST)
- public String login(HttpSession session, String kaptcha) {
- if(kaptcha.equals(session.getAttribute("kaptcha"))){
- return kaptcha + "验证码正确";
- }else{
- return kaptcha + "验证码错误";
- }
- }
- }
复制代码 添加登录页面- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
-
- </head>
- <body>
- <h3>请登录</h3>
- <input type="text" placeholder="请输入用户名" name="username" required="required"/>
- <br/>
- <input type="password" placeholder="请输入密码" name="password" required="required"/>
- <br/>
- <span style="display: inline">
- <input type="text" name="请输入验证码" id="kaptcha_value" placeholder="验证码" required="required"/>
- <img src="http://localhost:8301/demo/kaptcha" id="kaptcha" style="width:100px;height:50px;" class="mr-2"/>
- <a target="_blank" href="https://www.cnblogs.com/javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
- </span>
- <br/>
- <button type="submit" onclick="login()">登录</button>
- </body>
- </html>
复制代码 效果图
v自定义验证码文本天生器
创建自定义文本天生器- /**
- * @Author chen bo
- * @Date 2023/12
- * @Des
- */
- public class CustomTextCreator extends DefaultTextCreator {
- private static final String[] Number = "0,1,2,3,4,5,6,7,8,9,10".split(",");
- @Override
- public String getText()
- {
- int result;
- Random random = new Random();
- int x = random.nextInt(10);
- int y = random.nextInt(10);
- StringBuilder suChinese = new StringBuilder();
- int randomOperand = (int) Math.round(Math.random() * 2);
- if (randomOperand == 0) {
- result = x * y;
- suChinese.append(Number[x]);
- suChinese.append("*");
- suChinese.append(Number[y]);
- } else if (randomOperand == 1) {
- if (!(x == 0) && y % x == 0) {
- result = y / x;
- suChinese.append(Number[y]);
- suChinese.append("/");
- suChinese.append(Number[x]);
- } else {
- result = x + y;
- suChinese.append(Number[x]);
- suChinese.append("+");
- suChinese.append(Number[y]);
- }
- } else if (randomOperand == 2) {
- if (x >= y) {
- result = x - y;
- suChinese.append(Number[x]);
- suChinese.append("-");
- suChinese.append(Number[y]);
- } else {
- result = y - x;
- suChinese.append(Number[y]);
- suChinese.append("-");
- suChinese.append(Number[x]);
- }
- } else {
- result = x + y;
- suChinese.append(Number[x]);
- suChinese.append("+");
- suChinese.append(Number[y]);
- }
- suChinese.append("=?@").append(result);
- return suChinese.toString();
- }
- }
复制代码 更新Kaptcha配置类- /**
- * @Author chen bo
- * @Date 2023/12
- * @Des
- */
- @Configuration
- public class KaptchaConfig {
- // @Bean
- // public DefaultKaptcha getDefaultKaptcha() {
- // com.google.code.kaptcha.impl.DefaultKaptcha defaultKaptcha = new com.google.code.kaptcha.impl.DefaultKaptcha();
- // Properties properties = new Properties();
- // properties.put("kaptcha.border", "no");
- // properties.put("kaptcha.textproducer.font.color", "black");
- // properties.put("kaptcha.image.width", "200");
- // properties.put("kaptcha.image.height", "50");
- // properties.put("kaptcha.textproducer.font.size", "25");
- // properties.put("kaptcha.session.key", "verifyCode");
- // properties.put("kaptcha.textproducer.char.space", "5");
- // 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.key", "kaptchaCodeMath");
- // 验证码文本生成器
- properties.setProperty("kaptcha.textproducer.impl", "com.kaptcha.demo.util.CustomTextCreator");
- // 验证码文本字符间距 默认为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;
- }
- }
复制代码 创建CustomController- /**
- * @Author chen bo
- * @Date 2023/12
- * @Des
- */
- @RestController
- @Slf4j
- @RequestMapping("/custom")
- public class CustomController {
- @Autowired
- private Producer producer;
- public static final String DEFAULT_CODE_KEY = "random_code_";
- /**
- * @MethodName createCaptcha
- * @Description 生成验证码
- * @param httpServletResponse 响应流
- * @Author hl
- * @Date 2022/12/6 10:30
- */
- @GetMapping("/create")
- public void createCaptcha(HttpServletResponse httpServletResponse, HttpSession session) throws IOException {
- // 生成验证码
- String capText = producer.createText();
- String capStr = capText.substring(0, capText.lastIndexOf("@"));
- String result = capText.substring(capText.lastIndexOf("@") + 1);
- BufferedImage image = producer.createImage(capStr);
- // 保存验证码信息
- String randomStr = UUID.randomUUID().toString().replaceAll("-", "");
- System.out.println("随机数为:" + randomStr);
- //redisTemplate.opsForValue().set(DEFAULT_CODE_KEY + randomStr, result, 3600, TimeUnit.SECONDS);
- session.setAttribute("kaptcha", result);
- // 转换流信息写出
- FastByteArrayOutputStream os = new FastByteArrayOutputStream();
- try {
- ImageIO.write(image, "jpg", os);
- } catch (IOException e) {
- log.error("ImageIO write err", e);
- httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- // 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
- byte[] bytes = os.toByteArray();
- //设置响应头
- httpServletResponse.setHeader("Cache-Control", "no-store");
- //设置响应头
- httpServletResponse.setHeader("randomstr",randomStr);
- //设置响应头
- httpServletResponse.setHeader("Pragma", "no-cache");
- //在代理服务器端防止缓冲
- httpServletResponse.setDateHeader("Expires", 0);
- //设置响应内容类型
- ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
- responseOutputStream.write(bytes);
- responseOutputStream.flush();
- responseOutputStream.close();
- }
- }
复制代码 效果图 

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企服之家,中国第一个企服评测及商务社交产业平台。 |