一文搞定验证码(下部分)

news2025/2/25 10:27:20

文章目录

    • 1.背景
    • 2.验证
    • 3.valid接口具体实现类SimpleImageCaptchaValidator

1.背景

上一篇文章讲了验证码生成的逻辑. 验证码-上篇.
大概来说就是:

  1. 服务端保存一些默认的验证码图片. 然后需要生成时创建一个包含随机字符的验证码字符图片
  2. 根据随机字符和一些参数(如宽度、高度、图像类型等)生成验证码图片。图像生成通常使用 Java 的 Graphics 类或第三方库(如 Google 的 kaptcha 等)实现。
  3. 将图像数据编码为 Base64 或二进制流等格式,并返回给客户端。
  4. 客户端在需要验证用户输入时再次请求服务端,并将用户输入的验证码字符串传回。
  5. 服务端根据上一步存储的验证码字符串和用户输入进行比较,如果匹配成功则验证通过,否则不通过。

下面我们就讲一下验证的过程

2.验证

根据 tianai-captcha 中源码, 我们拉下来看ImageCaptchaValidator接口
源码

/**
 * @Author: 天爱有情
 * @date 2022/2/17 10:54
 * @Description 图片验证码校验器
 */
public interface ImageCaptchaValidator {

    /**
     * 计算滑块要背景图的百分比,基本校验
     *
     * @param pos    移动的位置
     * @param maxPos 最大可移动的位置
     * @return float
     */
    float calcPercentage(Number pos, Number maxPos);

    /**
     * 校验滑块百分比
     *
     * @param newPercentage 用户滑动的百分比
     * @param oriPercentage 正确的滑块百分比
     * @return boolean
     */
    boolean checkPercentage(Float newPercentage, Float oriPercentage);

    /**
     * 校验滑块百分比
     *
     * @param newPercentage 用户滑动的百分比
     * @param oriPercentage 正确的滑块百分比
     * @param tolerant      容错值
     * @return boolean
     */
    boolean checkPercentage(Float newPercentage, Float oriPercentage, float tolerant);

    /**
     * 用于生成验证码校验时需要的回传参数
     *
     * @param imageCaptchaInfo 生成的验证码数据
     * @return Map<String, Object>
     */
    Map<String, Object> generateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo);

    /**
     * 校验用户滑动滑块是否正确
     *
     * @param imageCaptchaTrack      包含了滑动轨迹,展示的图片宽高,滑动时间等参数
     * @param sliderCaptchaValidData generateSliderCaptchaValidData(生成的数据
     * @return boolean
     */
    boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData);
}

上面最主要的接口是 boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData); 验证接口. 来判断校验用户滑动滑块是否正确

3.valid接口具体实现类SimpleImageCaptchaValidator

校验源码

/**
* 验证
**/
@Override
    public boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
        // 读容错值
        Float tolerant = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant);
        // 读验证码类型
        String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER);
        // 验证前
        // 在验证前必须读取 容错值 和验证码类型
        if (!beforeValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type)) {
            return false;
        }
        Integer bgImageWidth = imageCaptchaTrack.getBgImageWidth();
        if (bgImageWidth == null || bgImageWidth < 1) {
            // 没有背景图片宽度
            return false;
        }
        List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();
        if (CollectionUtils.isEmpty(trackList)) {
            // 没有滑动轨迹
            return false;
        }
        // 验证
        boolean valid = doValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
        if (valid) {
            // 验证后
            valid = afterValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
        }
        return valid;
    }

验证实现类中, 具体校验逻辑在 doValid() 方法中,最后两参数是容错值和校验类型

public boolean doValid(ImageCaptchaTrack imageCaptchaTrack,
                           Map<String, Object> sliderCaptchaValidData,
                           Float tolerant,
                           String type) {
        if (CaptchaUtils.isSliderCaptcha(type)) {
            // 滑动类型验证码
            return doValidSliderCaptcha(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
        } else if (CaptchaUtils.isClickCaptcha(type)) {
            // 点选类型验证码
            return doValidClickCaptcha(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
        }
        // 不支持的类型
        log.warn("校验验证码警告, 不支持的验证码类型:{}, 请手动扩展 cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator.doValid 进行校验扩展", type);
        return false;
    }

我们看滑动类型验证码方法 doValidSliderCaptcha

/**
     * 校验滑动验证码
     *
     * @param imageCaptchaTrack      sliderCaptchaTrack
     * @param sliderCaptchaValidData sliderCaptchaValidData
     * @param tolerant               tolerant
     * @param type                   type
     * @return boolean
     */
    public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack,
                                        Map<String, Object> sliderCaptchaValidData,
                                        Float tolerant,
                                        String type) {
        // 读取百分比
        Float oriPercentage = getFloatParam(PERCENTAGE_KEY, sliderCaptchaValidData);
        if (oriPercentage == null) {
            // 没读取到百分比
            return false;
        }
        List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();
        // 取最后一个滑动轨迹
        ImageCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
        // 计算百分比
        float calcPercentage = calcPercentage(lastTrack.getX(), imageCaptchaTrack.getBgImageWidth());
        // 校验百分比
        return checkPercentage(calcPercentage, oriPercentage, tolerant);
    }

// 校验百分比是否符合
@Override
    public boolean checkPercentage(Float newPercentage, Float oriPercentage, float tolerant) {
        if (newPercentage == null || Float.isNaN(newPercentage) || Float.isInfinite(newPercentage)
                || oriPercentage == null || Float.isNaN(oriPercentage) || Float.isInfinite(oriPercentage)) {
            return false;
        }
        // 容错值
        float maxTolerant = oriPercentage + tolerant;
        float minTolerant = oriPercentage - tolerant;
        return newPercentage >= minTolerant && newPercentage <= maxTolerant;
    }

这里是基础的滑动模块校验. 校验了滑动的位置是否符合校验值.比较简单
下面我们来看下 点选模块的校验,就是在doValid方法中 doValidClickCaptcha

/**
     * 校验点选验证码
     *
     * @param imageCaptchaTrack      sliderCaptchaTrack
     * @param sliderCaptchaValidData sliderCaptchaValidData
     * @param tolerant               tolerant
     * @param type                   type
     * @return boolean
     */
    public boolean doValidClickCaptcha(ImageCaptchaTrack imageCaptchaTrack,
                                       Map<String, Object> sliderCaptchaValidData,
                                       Float tolerant,
                                       String type) {
        String validStr = getStringParam(PERCENTAGE_KEY, sliderCaptchaValidData, null);
        if (ObjectUtils.isEmpty(validStr)) {
            return false;
        }
        String[] splitArr = validStr.split(";");
        List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();
        if (trackList.size() < splitArr.length) {
            return false;
        }
        // 取出点击事件的轨迹数据
        List<ImageCaptchaTrack.Track> clickTrackList = trackList
                .stream()
                .filter(t -> TrackTypeConstant.CLICK.equalsIgnoreCase(t.getType()))
                .collect(Collectors.toList());
        if (clickTrackList.size() != splitArr.length) {
            return false;
        }
        for (int i = 0; i < splitArr.length; i++) {
            ImageCaptchaTrack.Track track = clickTrackList.get(i);
            String posStr = splitArr[i];
            String[] posArr = posStr.split(",");
            float xPercentage = Float.parseFloat(posArr[0]);
            float yPercentage = Float.parseFloat(posArr[1]);

            float calcXPercentage = calcPercentage(track.getX(), imageCaptchaTrack.getBgImageWidth());
            float calcYPercentage = calcPercentage(track.getY(), imageCaptchaTrack.getBgImageHeight());
			// 判断每个点选值是否符合范围
            if (!checkPercentage(calcXPercentage, xPercentage, tolerant)
                    || !checkPercentage(calcYPercentage, yPercentage, tolerant)) {
                return false;
            }
        }
        return true;
    }

这段代码实现了校验点选验证码的功能,用于检查用户完成滑块验证时是否存在异常。具体而言,该方法接收一个滑块验证码的图片轨迹数据和验证信息等参数,通过计算出用户实际点击位置与给定位置之间的误差,并根据设置的容错范围判断该验证码是否通过验证。

具体流程如下:

  1. 首先从验证信息中获取验证位置的百分比,并检测是否为空。
  2. 根据返回结果分别获取用户完成验证时的轨迹列表和当前验证位置的数量。
  3. 检查验证位置的数量是否与点击事件的位置数一致。
  4. 对于每个点击事件,将其实际位置的百分比计算出来,并比较它与预期位置的误差值是否在可接受的容错范围内。
  5. 若全部验证通过,则返回true,否则返回false。

需要注意的是,这段代码仅提供了部分验证功能,并不能保证完全有效。因此,在实现安全性要求更高的验证码时,还需要考虑其他安全机制以防止恶意攻击
来源-天爱有情验证码

当然你也可以自己实现ImageCaptchaValidator类, 做自己定制化校验规则.
至此验证码生成,验证逻辑就梳理完了.
由于本人才疏学浅, 文章难免有错误的地方, 欢迎指正~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新入职一个00后卷王,每天加班到2点,太让人崩溃了····

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

chatgpt赋能python:PythonScheme:为什么你应该考虑在你的下一个项目中使用它

Python Scheme&#xff1a;为什么你应该考虑在你的下一个项目中使用它 介绍 Python是一种常用的编程语言&#xff0c;被广泛应用于各种应用程序&#xff0c;包括Web开发、数据分析和人工智能。今天&#xff0c;我们要讨论的是Python编程语言的一种方言&#xff0c;名为“Pyth…

HR不会告诉你!Java程序员月薪8K和20K的区别!

昨天有同学问好程序员&#xff0c;为啥都是干Java程序员&#xff0c;别人可以拿20k&#xff0c;我才拿8k呢&#xff1f;为啥人家能提前转正我就得晚俩月&#xff1f;小源一听大事不妙&#xff0c;赶紧连夜整理了以下清单供大家check&#xff01; 对于刚入职场还有跳槽成功的同学…

【2023 · CANN训练营第一季】昇腾AI入门课(TensorFlow)微认证考试

1、下面哪个AI框架开发模型可以不用适配&#xff0c;直接在昇腾AI处理器上进行训练&#xff1f; A. PyTorch B. Caffe C. Mindspore D. Tensorflow C 2、使用Estimator进行脚本训练开发的一般步骤为&#xff08;A&#xff09; A. 数据预处理 --> 模型构建 --> 运行配置 -…

苏诗:医疗器械企业增长秘籍之CRM系统的 4 大能力建设

近些年&#xff0c;在国家医疗产业政策支持的推动下&#xff0c;医疗器械产业已进入蓬勃发展的“黄金时期”&#xff0c;医疗器械产品丰富度增加&#xff0c;配套服务体系逐渐完善&#xff0c;国产品牌效应进一步凸显。 医疗健康行业是纷享的战略行业之一&#xff0c;自2017年…

chatgpt如何自动生成角色prompt模板

chatgpt如何自动生成角色prompt模板 作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 一、角色prompt模板 下面是套图的chatgpt模板&#xff0c;你可以手动的进行填写。将角色的描述填…

如何把数据从 TDengine 2.x 迁移到 3.x ?

一.迁移背景&#xff1a; 随着时序数据库&#xff08;Time Series Database&#xff09; TDengine 3.0 的发布至今&#xff0c;我们除了在持续地优化产品质量的本身&#xff0c;也一直在努力地提升用户体验。但由于 3.0 底层有大量的重构优化&#xff0c;导致开源版的 2.0 用户…

集权攻击-无权限条件下AD域凭据获取与利用分析

前言 对AD域攻击的前期&#xff0c;在没有任何域内据点或域用户凭据时&#xff0c;攻击者往往会使用用户名枚举、密码爆破、密码喷洒、Roasting等手段进行域用户凭据的窃取&#xff0c;本篇文章将针对AD域攻击时无权限环境下对域用户的信息收集及凭据窃取进行分析。 用户名枚举…

四、CNNs网络架构-深度可分离卷积

《A review of convolutional neural network architectures and their optimizations》论文指出一些高性能的卷积神经网络方法不可避免地带来巨大的计算成本&#xff0c;往往需要高性能GPU或高度优化的分布式CPU架构的支持。尽管CNNs应用向移动终端扩展&#xff0c;但大多数移…

如何在Spring Boot服务端实现公网远程调试并进行HTTP服务监听

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…

Go语言 -- Web开发基础学习 net/http包

前言 Go 是一个开源的编程语言&#xff0c;它能让构造简单、可靠且高效的软件变得容易。 Go语言最擅长的领域就是Web开发&#xff0c;此贴是本人入门完go语法基础后学习Web开发的学习笔记。 第一个Go Web 新建go文件hello_world.go 写入&#xff1a; package mainimport (&…

企业级信息系统开发——初探Spring AOP

文章目录 一、提出游吟诗人唱赞歌任务&#xff08;一&#xff09;采用传统方式实现&#xff08;二&#xff09;采用传统方式实现的缺点 二、采用配置方式使用AOP&#xff08;一&#xff09;创建本讲所需子包&#xff08;二&#xff09;创建杀龙任务类&#xff08;三&#xff09…

VESD静电监控系统:提高静电防护效果与管理效率

随着科学技术不断发展&#xff0c;现代的工业对静电防护的要求越来越高。因为静电的存在可能会对产品质量、工作环境、甚至是人身产生威胁。静电监控系统是一项高效的管理工具&#xff0c;能够有效地控制和监测静电产生的情况&#xff0c;提高静电防护效果和管理效率。 VESD静电…

群晖DS920 video station使用教程

群晖DS920 video station使用教程 为了更好的浏览体验&#xff0c;欢迎光顾勤奋的凯尔森同学个人博客http://www.huerpu.cc:7000 安装video station在群晖套件里点一下就好&#xff0c;这里不说了。 一、添加视频库 可以添加电视剧、电视节目等类型。 比如我在国产剧这个视频…

写出诗一样代码的灵丹妙药: const与assert

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《C知识系统分享》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c;…

网络安全好就业吗?会不会容易被淘汰?

网络安全发展前景好吗&#xff1f;好就业吗&#xff1f;会不会容易被淘汰&#xff1f;很多刚入门或者想入门的小伙伴都会有这样的疑问 今天就在这里给大家介绍一下。我就在这个行业&#xff0c;目前湖南&#xff0c;薪资就没必要说了&#xff0c;高就对了。 这个行业优势就是工…

互联网营销推荐算法理论-30分钟理解协同过滤

一、协同过滤概念 不论在淘宝还是京东&#xff0c;你浏览了/购买了某个商品A&#xff0c;后面几天你在该app内总是会在首页看到商品A和商品A相似的商品&#xff0c;背后支撑这种能力的就是推荐系统&#xff0c;而其推荐算法可能就是协同过滤。&#xff08;注&#xff1a;app内…

Scala学习(九)---List集合

文章目录 1.List1.1 不可变List集合1.2 可变集合ListBuffer 1.List List集合默认为不可变集合&#xff0c;List集合在实例化的时候&#xff0c;无法通过new关键字进行实例化&#xff0c;只能通过伴生apply方法来对其进行实例化 1.1 不可变List集合 创建一个不可变list集合 …

真题详解(树的结点)-软件设计(八十四)

真题详解&#xff08;汇总&#xff09;-软件设计&#xff08;八十三)https://blog.csdn.net/ke1ying/article/details/130856130?spm1001.2014.3001.5501 COCOMOII估算不包括_____。 对象点 B.功能点 C.用例数 D.源代码行 答案&#xff1a;C 语法翻译是一种&#xff…

【vue3】 实现 公共搜索组件,在当前页搜索的路由跳转不能改变当前值的操作,使用bus / event-emitter 派发器

一、安装 bus 插件 cnpm install --save event-emitter 二、创建 bus.ts 文件 1、在utils下创建bus.ts 2、bus.ts 代码如下 import ee from event-emitter export default ee() 三、页面使用 1、搜索的公用组件页面&#xff0c;search.vue <el-input v-model.trim&qu…