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

news/2024/10/31 9:28:50/

文章目录

    • 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

校验源码

/**
* 验证
**/
@Overridepublic 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);}// 校验百分比是否符合
@Overridepublic 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.ppmy.cn/news/83546.html

相关文章

三、Go的常用命令以及Go的执行原理

Go的执行原理以及Go的命令 一、Go的源码文件 Go 的源码文件分类&#xff1a; 如上图&#xff0c;分为三类&#xff1a; 1、命令源码文件&#xff1a; 声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。 命令源码文件被安装以后&#xff0c;GOPATH 如果…

C++可变模板参数的使用

最近在封装打印函数&#xff0c;需要支持不同参数个数和类型&#xff0c;用了函数重载感觉有点麻烦且不太能满足所有的条件。研究了一下C11的新特性可变模板参数 模板定义&#xff1a;声明可变参数模板时需要在typename或class后面带上省略号“…”。 template <class… T&…

中国人民大学与加拿大女王大学金融硕士——在职人员成长路上的选择与追求

在职人员职场充电&#xff0c;选择中国人民大学与加拿大女王大学金融硕士项目是正确的吗&#xff1f;其实每个选择都有各自的收获和代价&#xff0c;不能简单的用“优劣”、“好坏”去衡量。要看这个选择是否给给予你想要的&#xff0c;还要看这个选择的代价是否是你愿意承受并…

如何在华为OD机试中获得满分?Java实现【计算某字符出现次数】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

还没搞懂重写和重载吗?这篇文章可以帮助你

文章目录 前言一、向上转型1.概念&#xff1a;2.CODE 二、重写三.重载四.动态和静态绑定总结 前言 首先&#xff0c;会大致介绍一下什么叫做向上转型&#xff0c;方便后续的理解 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、向上转型 1.概念&a…

JQuery实现小项目

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、JQuery是什么 二、JQuery项目 2.1 猜数字 2.2 表白墙 2.3 聚合搜索 2.4 计算器 一、JQuery是什么 jQuery是一个快速、简洁的JavaScript框架&#xff0c;是继Prototype之…

Java设计模式七大原则-单一职责原则

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 单一职责原则 1、单一职责介绍 单一职责原则&#xff08;SRP&#xff1a;Single Responsibility Principle&#xff09;是指一个类…

【C语言】实现猜数字游戏——随机数

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;C语言 该篇将对 选择与循环语句 进行运用&#xff0c;实现猜数字游戏。 需求&#xff1a;游戏后可以选择再次进行游戏&#xff0c;也可以选择…