图形验证码从设计到实现

在这里插入图片描述

验证码的使用场景

图形验证码在我们的日常使用中是非常常见的,比如一些App、小程序、PC网站等。涉及到的业务也比较广泛、例如用户登录流程、注册流程、找回密码。下面我们来大致梳理下上述流程:

  1. 登录流程
  • 用户首先在登录界面输入手机号
  • 然后通过图形验证码验证,只有成功通过验证码后才能继续下一步
  • 成功通过图形验证码后,系统向用户手机发送一条包含数字验证码的短信
  • 用户收入收到的验证码,系统验证无误后允许登录
  1. 注册流程
  • 用户填写基本信息,包括手机号
  • 提交信息前需要通过图形验证码验证
  • 通过图形验证码后,系统发送短信验证码到用户手机
  • 用户输入短信验证码完成注册过程
  1. 找回密码
  • 用户输入与账号关联的手机号
  • 进行图形验证码验证
  • 通过验证后,系统发送密码重置链接或重置密码所需的验证码至用户的手机
  • 用户按照指引重置密码

验证码的生成步骤

知道了验证码的使用区域,下面来简单说下验证码的生成

  1. 背景生成
    • 选择一个随机颜色作为背景色。
    • 可以添加一些随机的干扰线条或点来增加破解难度。
  2. 字符生成
    • 从预设的字符集中随机选取几个字符组成验证码字符串。这些字符可以是数字、小写字母或大写字母
    • 对于每个字符,可以随机选择不同的字体、大小以及倾斜度。
  3. 字符渲染
    • 将生成的字符绘制在背景上。为了进一步提高安全性,可以对字符应用扭曲效果或者随机旋转一定角度
    • 字符之间的间距也应该是随机的,以便于增加识别难度
  4. 噪声添加
    • 在背景中随机位置添加噪声点或线,这有助于迷惑OCR(光学字符识别)工具。
    • 可以使用不同的颜色和形状,使得噪声更加自然。
  5. 图形变形
    • 整个图像可以应用轻微的扭曲或变形,以使得验证码更难以被自动化工具识别。
  6. 输出图像
    • 最后将处理好的图像输出为JPEG或PNG格式,并且可能还需要设置适当的分辨率和压缩级别以保证质量
  7. 存储验证码值及过期机制
    • 验证码的实际文本需要存储在服务器端,并且通常会关联一个会话ID或者令牌,这样当用户提交表单时,可以验证输入是否正确
    • 设置验证码的有效时间,超出这个时间则认为验证码无效。

验证码的代码实现

创建springBoot项目,导入kaptcha相关依赖

 <dependency>
    <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>kaptcha-spring-boot-starter</artifactId>
      <version>1.1.0</version>
  </dependency>

配置验证码的生成方法

/**
 * @Author: LiFly
 * @Date: 2024/8/27 17:05
 * @Description:
 */
@Configuration
public class CaptchaConfig {

    private static final String CHAR_LENGTH = "4";

    private static final String CHAR_SPACE = "8";

    private static final String CAPTCHA_NOISE_IMPL = "com.google.code.kaptcha.impl.NoNoise";

    private static final String CAPTCHA_SCARIFICATION_IMPL = "com.google.code.kaptcha.impl.WaterRipple";

    private static final String KAPTCHA_TEXTPRODUCER_CHAR_STRING = "0123456789";

    /**
     * 验证码配置
     * @return 获取默认验证码配置
     */
    @Bean
    @Qualifier("captchaProducer")
    public DefaultKaptcha kaptcha(){
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        //验证码个数
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH,CHAR_LENGTH);
        //字体间隔
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,CHAR_SPACE);

        //干扰实现类
        properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, CAPTCHA_NOISE_IMPL);

        //图片样式
        properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, CAPTCHA_SCARIFICATION_IMPL);

        //文字来源
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING,KAPTCHA_TEXTPRODUCER_CHAR_STRING);

        Config config = new Config(properties);

        kaptcha.setConfig(config);

        return kaptcha;
    }
}

配置redis的序列化方式

/**
 * @Author: LiFly
 * @Date: 2024/8/27 16:39
 * @Description:
 */
@Configuration
public class RedisTemplateConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //使用Jackson2JsonRedisSerialize替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        //设置key和value的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        //设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //设置支持事务
        redisTemplate.setEnableTransactionSupport(true);

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}

redis相关链接配置也加上

spring.redis.host=127.0.0.1
spring.redis.port=6379
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active = 10
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle = 10
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle = 0
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait= -1ms
#指定客户端
spring.redis.client-type = lettuce
#配置文件指定缓存类型
spring.cache.type=redis

下面我们来开发获取验证码的接口

 @ApiOperation("获取验证码")
    @GetMapping("/getCaptcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {
        //创建验证码
        String captchaText = captchaProducer.createText();
        //缓存验证码
        redisTemplate.opsForValue().set(getCaptchaKey(request), captchaText,CAPTCHA_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        //创建图形验证码
        BufferedImage bufferedImage = captchaProducer.createImage(captchaText);
        //返回图形验证码
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
            ImageIO.write(bufferedImage,"jpg",out);
            out.flush();
            out.close();
        }catch (Exception e){
            log.error("获取图形验证码失败!e={}",e.getMessage());
        }
    }

 /**
     * 获取缓存key
     * @param request 请求参数
     * @return 组装key
     */
    private String getCaptchaKey(HttpServletRequest request){
        String ip = CommonUtil.getIpAddr(request);
        String userAgent = request.getHeader("User-Agent");
        return "user-service:captcha:" + CommonUtil.MD5(ip+userAgent);
    }

获取验证码的接口非常简单,调用验证码配置类获取验证码,将获取到的验证码缓存到redis中并设置过期时间,然后调用配置类传入验证码获取到图形验证码,通过IO流返回。下面校验验证码

   @ApiOperation("校验验证码")
    @PostMapping("/sendCode")
    public JsonData sendCode(@RequestParam(value = "to",required = true)String to,
                             @RequestParam(value = "captcha",required = true) String captcha,
                             HttpServletRequest request) {
        String key = getCaptchaKey(request);
        String cacheCaptcha = redisTemplate.opsForValue().get(key);

        if (cacheCaptcha != null && captcha != null && cacheCaptcha.equals(captcha)) {
            redisTemplate.delete(key);
            //发送验证码
            return JsonData.buildSuccess();
        }else {
            return JsonData.buildResult("图形验证码错误");
        }

    }

获取前端传过来的手机号以及图形验证码,根据ip以及网络地址相关西信息获取缓存key,根据key获取验证码,然后比较验证码是否一致,如果一致验证通过,删除缓存,执行发送短信相关业务。如果不一致直接返回给前端错误信息。

接口测试

下面我们通过postman来测试下开发的接口,首先获取验证码:
在这里插入图片描述
查看到获取的验证码为5437,再去查看下redis是否存在:
在这里插入图片描述
redis也存在刚才获取的验证码,说明我们获取验证码的接口是没问题的。
再去看下校验验证码的这个接口,
在这里插入图片描述
可以看到,校验验证码的接口也是没问题的,校验验证码后,里面会删除缓存,这时候再来看下缓存中是否还存在:
在这里插入图片描述
刷新缓存为空了,说明检验验证码的接口逻辑是没问题的。后续就可以执行发送短信验证码了。
上述我们主要讲述了用户登录获取验证码的逻辑,用户注册,找回密码逻辑都是差不多一样的,只是后续的处理不太一样,在这里就不再过多讲述了。
总之,图形验证码与手机号验证码的结合使用,不仅增强了系统的安全性,也为用户提供了便捷的操作体验。未来,随着更多创新技术的应用,验证码系统将会变得更加智能和人性化,更好地服务于广大用户。希望本文能为开发者们提供一些有价值的参考,帮助大家在实际工作中构建更加稳固的安全防线。
更多精彩内容请关注以下公众号
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/872996.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

心脑血管科张景龙医生:心功能不全,全面解析心脏健康的隐忧

在探讨人体健康的众多话题中&#xff0c;心功能不全是一个不容忽视的重要议题。它不仅关乎心脏这一生命之泵的核心功能&#xff0c;还直接影响到我们的生活质量乃至生命安全。本文旨在以权威、清晰、通俗且实用的方式&#xff0c;全面解析心功能不全的含义、成因、症状、诊断及…

单元测试 Mock不Mock?

文章目录 前言单元测试没必要?Mock不Mock?什么是Mock?Mock的意义何在? 如何Mock&#xff1f;应该Mock什么&#xff1f;Mock 编写示例 总结 前言 前段时间&#xff0c;我们团队就单元测试是否采用 Mock 进行了一番交流&#xff0c;各有各的说法。本文就单元测试 Mock不Mock…

EmguCV学习笔记 VB.Net 9.2 VideoWriter类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结&#xff0c;删除链表节点问题使用到列表&#xff0c;哈希表&#xff0c;递归比较容易超时&#xff0c;我觉得使用计数排序比较稳&#xff0c;处理起来也不是很难。 1. 力扣3217&#xff1a;从链表中移除在数组中的节点 1.1 题目&#xff1a; 给你一个整数数组 nums 和一…

Azure AI Search 中的二进制量化:优化存储和加快搜索速度

随着组织继续利用生成式 AI 的强大功能来构建检索增强生成 (RAG) 应用程序和代理&#xff0c;对高效、高性能和可扩展解决方案的需求从未如此强烈。 今天&#xff0c;我们很高兴推出二进制量化&#xff0c;这项新功能可将向量大小减少高达 96%&#xff0c;同时将搜索延迟减少高…

集合及映射

1、集合类图 1&#xff09;ArrayList与LinkedList 区别 LinkedList 实现了双向队列的接口&#xff0c;对于数据的插入速度较快&#xff0c;只需要修改前后的指向即可&#xff1b;ArrayList对于特定位置插入数据&#xff0c;需要移动特定位置后面的数据&#xff0c;有额外开销 …

Spring Boot-自定义banner

在 Spring Boot 应用中&#xff0c;你可以自定义启动时显示的 banner。这些 banner 可以包括图形、文字或者其他形式的标识。如图所示&#xff1a; 1. 使用 banner.txt 文件 默认情况下&#xff0c;Spring Boot 使用项目的 banner.txt 文件中的内容作为启动时的 banner。你可以…

Verilog语法+:和-:有什么用?

Verilog语法:和-:主要用于位选择&#xff0c;可以让代码更简洁。 一、位选择基础 在Verilog中&#xff0c;位选择可以通过直接索引来实现&#xff0c;例如&#xff1a; reg [7:0] data; wire select_a; wire [2:0] select_b; assign select_a data[3]; assign select_b …

【Hot100】LeetCode—739. 每日温度

目录 1- 思路单调栈 2- 实现⭐739. 每日温度——题解思路 3- ACM 实现 原题链接&#xff1a;739. 每日温度 1- 思路 单调栈 寻找当前位置的下一个比他大的元素的位置利用 Stack 栈维护一个单调栈 当前元素 < 栈顶元素&#xff0c;直接压栈否则 利用 while 循环判断 2- 实…

备忘录怎么隐藏起来 适合记录秘密的备忘录推荐

备忘录作为我们日常生活中常用的记录工具&#xff0c;经常被用来记录重要资料或工作事项。然而&#xff0c;当我们需要在备忘录中记录一些涉及个人隐私的内容时&#xff0c;如何确保这些信息不被他人轻易窥视&#xff0c;保护内容的安全就显得尤为重要。 想象一下&#xff0c;…

【数学建模国赛思路预约】2024全国大学生数学建模竞赛助攻思路、代码、论文

2024年全国大学生数学建模大赛马上就要开始了&#xff0c;大家有没有准备好呢&#xff0c;今年将会和之前一样&#xff0c;将会在比赛赛中时期为大家提供比赛各题的相关解题思路、可运行代码参考以及成品论文。 一、分享计划表如下所示 1、 赛中分享内容包括&#xff08;2023国…

【LeetCode】06.Z字形变换

题目要求 解题思路 首先映入我们脑海的就是暴力。这一方法可行&#xff0c;但是时间复杂度空间复杂度很高&#xff0c;因此我们使用找规律的方法。这样的话我们可以模拟插入下标&#xff0c;这样的话很容易发现首行和末行插入的位置刚好是d2*n-2&#xff0c;而中间行的两个位置…

视频编码与传输 学习笔记 1 一些视频压缩算法的介绍

大概是这么个结构&#xff1a; 说白了&#xff0c;就是视频太大&#xff0c;不压缩不行&#xff0c;因此我们会用压缩比非常夸张但对于视频来说效果很好的压缩方法先对视频压缩&#xff08;source coding&#xff09;然后把压缩后的视频发出去&#xff0c;要看的时候再解压。 就…

3. GIS后端工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

交换机自动化备份配置(H3C_无人值守)

介绍&#xff1a; 在日常运维过程中&#xff0c;需要定时备份设备的配置&#xff0c;在设备数量过于庞大的情况下&#xff0c;对我们的运维工作会造成极大地不便&#xff0c;通过python自动化能够完美解决人工手动保存设备配置的问题。而且自动化运维在未来也一定是大势所趋&a…

STM32G474之TIM1输出PWM信号支持互补输出,死区时间和刹车

STM32G474之TIM1输出PWM信号&#xff0c;互补输出&#xff0c;支持死区时间和刹车。PWM第1通道输出引脚配置&#xff1a;TIM1_CH1映射到PA8,TIM1_CH1N映射到PA7&#xff0c;TIM1_BKIN映射到PA6&#xff0c;用作刹车输入信号。当刹车时&#xff0c;停止PWM波形输出。在使用“比较…

JAVAEE初阶第六节——网络编程套接字

系列文章目录 JAVAEE初阶第六节——网络编程套接字 文章目录 系列文章目录JAVAEE初阶第六节——网络编程套接字 一. 网络编程基础1. 为什么需要网络编程2. 什么是网络编程3.网络编程中的基本概念 3.1 发送端和接收端 3.2 请求和响应 3.3 客户端和服务端 4. 常见的客户端服务…

【2024数模国赛赛题思路公开】国赛C题第三套思路丨无偿自提

C题参考思路 C题是一道优化问题&#xff0c;目的是根据题目所给的种植限制条件以及附件数据建立目标条件优化模型&#xff0c;优化种植策略&#xff0c;有利于方便田间管理&#xff0c;提高生产效益&#xff0c;减少各种不确定因素可能造成的种植风险。整个题目最重要的问题在…

英语每日一段 195

Promising economic indicators won’t instantly reverse the lingering impact of hard times for millions of families, workplace culture expert Jessica Kriegel said. “Perception and reality are sometimes aligned and sometimes not,” Kriegel told Newsweek. “…

C语言中的预处理指令中的其中一对——#ifdef和#ifndef

目录 开头1.什么是#ifdef和#ifndef?2.#ifdef和#ifndef的实际应用判断ABCD这个宏是否被定义过判断HELLO这个宏是否没被定义过防止头文件重复定义 下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。今天&#xff0c;我们要学一下关于C语言中的预处理指令中的其中一对…