首页 > 其他分享 >SpringBoot进阶教程(八十三)Kaptcha

SpringBoot进阶教程(八十三)Kaptcha

时间:2024-11-16 21:19:30浏览次数:1  
标签:suChinese SpringBoot Kaptcha 八十三 验证码 kaptcha com properties impl

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 , no yes
kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness 边框厚度,合法值:>0 1
kaptcha.image.width 图片宽 200
kaptcha.image.height 图片高 50
kaptcha.producer.impl 图片实现类 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.font.size 文本实现类 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.font.size 字体大小 40px.
kaptcha.textproducer.font.color 字体颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.textproducer.char.space 文字间隔 2
kaptcha.noise.impl 干扰实现类 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干扰 颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景实现类 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景颜色渐变,开始颜色 light grey
kaptcha.background.clear.to 背景颜色渐变, 结束颜色 white
kaptcha.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>
    <script>
        function refresh_kaptcha() {
            var path = "http://localhost:8301/demo/kaptcha?p=" + Math.random();
            document.getElementById("kaptcha").src=path;
        }

        function login() {
            var xhr = new XMLHttpRequest;

            xhr.open('post', 'http://localhost:8301/demo/login');
            // post请求的参数要放在send方法中作为参数的 - 必须的字符串
            // post请求要带参数必须在send之前设置 头信息
            xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
            // 数据在传送之前需要进行编码
            xhr.send('kaptcha=' + document.getElementById("kaptcha_value").value)
            xhr.onreadystatechange = function () {
                // 监听请求状态的变化 readystate (1-5 1准备发送 2 发送完成 3 发送完成数据准备接收 4数据
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        var res = xhr.responseText;
                        //res = JSON.parse(res)
                        alert(res);
                    }
                }
            }
        }
    </script>
</head>
<body>
<h3>请登录</h3>
<div class="col-sm-4">
    <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 href="javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
    </span>
    <br/>
    <button type="submit" onclick="login()">登录</button>
</div>
</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前后端验证码实现流程扩展

  1. 前端向后端请求验证码。
  2. 后端通过谷歌开源工具Kaptcha生成图形验证码(实际是5个随机字符),缓存到redis,key键可以是 业务+用户id,value值就是那5个随机字符。设置TTL为2分钟。
  3. 后端将图形验证码转base64编码字符串,将该字符串返回给前端。
  4. 前端解析base64编码的字符串,即可在页面上显示图形验证码。
  5. 用户输入密码与验证码后提交表单到后端。
  6. 后端根据业务和用户id组成的key键到redis查找缓存的验证码信息,会有如下情况:
    • 如果redis返回为空,则通知前端验证码失效,需要重新获取验证码。
    • 如果redis返回不为空,但是不相等,说明验证码输入错误。则删除redis中对应验证码缓存,通知前端验证码错误,需要重新获取验证码。
    • 如果redis返回不为空,并且相等,则校验成功,就删除redis中对应验证码缓存,并在mysql中修改密码。最后通知前端修改成功。

其他参考/学习资料:

v源码地址

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


作  者:请叫我头头哥
出  处:http://www.cnblogs.com/toutou/
关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

标签:suChinese,SpringBoot,Kaptcha,八十三,验证码,kaptcha,com,properties,impl
From: https://www.cnblogs.com/toutou/p/SpringBoot_Kaptcha.html

相关文章

  • springboot毕设 面向社区的洗衣店智能服务系统设计与实现808ub程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加快,社区居民对于生活服务的便捷性和智能化需求日益增长。洗衣作为日常生活中的一项重要服务,传统的洗衣店模式已难以满足现代人追求......
  • springboot毕设 门诊电子处方管理系统 程序+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景:随着医疗信息化的发展,传统的手工处方管理方式已难以满足现代门诊的高效运作需求。门诊作为医疗机构的前沿阵地,每天需要处理大量的患者信息和处方数据......
  • springboot3整合mybatisplus问题Invalid value type for attribute 'factoryBeanObjec
    版本说明:JDK版本:17springboot版本:3.3.5问题分析:springboot版本与mybatisplus版本不兼容解决办法:将mybatisplus版本替换为以下版本<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>......
  • 【2024最新】基于springboot+vue的心灵治愈交流平台lw+ppt
    作者:计算机搬砖家开发技术:SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等,“文末源码”。专栏推荐:SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码精品专栏:Java精选实战项目源码、Python精选实战项目源码、大数据精选实战项目源码......
  • 基于SpringBoot+Vue实现剧本杀服务平台【源码+LW+PPT+部署】
     作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企......
  • SpringBoot云南省大学生支教平台017q3程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景云南省部分偏远地区教育资源匮乏,师资力量薄弱,制约了当地教育事业的发展。随着大学生社会责任感的增强,越来越多的青年选择投身支教事业......
  • SpringBoot游泳馆会员管理系统q26c5(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着健康意识的提升,游泳馆作为重要的健身场所,其会员管理效率直接影响到顾客体验和经营效益。传统的会员管理方式存在信息记录不......
  • 基于SpringBoot+Vue实现校园多媒体信息共享平台
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企业......
  • springboot毕设宠物服务一体化系统的设计与实现程序+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着社会的发展和人们生活水平的提高,宠物在人们生活中的地位日益重要。近年来,宠物市场规模不断扩大,涵盖了宠物交易、宠物服务、宠物用品等多个领......
  • springboot毕设高校双创管理系统pc端 源码+论文+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着高校创新创业教育的不断发展,培养具有创新精神和创业能力的高素质人才成为高校的重要任务之一。传统的管理模式难以满足双创活动日益增长的需......