上一篇文章讲了验证码生成的逻辑. 验证码-上篇.
大概来说就是:
下面我们就讲一下验证的过程
根据 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
*/
Map generateImageCaptchaValidData(ImageCaptchaInfo imageCaptchaInfo);
/**
* 校验用户滑动滑块是否正确
*
* @param imageCaptchaTrack 包含了滑动轨迹,展示的图片宽高,滑动时间等参数
* @param sliderCaptchaValidData generateSliderCaptchaValidData(生成的数据
* @return boolean
*/
boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map sliderCaptchaValidData);
}
上面最主要的接口是 boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map
验证接口. 来判断校验用户滑动滑块是否正确
/**
* 验证
**/
@Override
public boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map 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 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 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 sliderCaptchaValidData,
Float tolerant,
String type) {
String validStr = getStringParam(PERCENTAGE_KEY, sliderCaptchaValidData, null);
if (ObjectUtils.isEmpty(validStr)) {
return false;
}
String[] splitArr = validStr.split(";");
List trackList = imageCaptchaTrack.getTrackList();
if (trackList.size() < splitArr.length) {
return false;
}
// 取出点击事件的轨迹数据
List 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;
}
这段代码实现了校验点选验证码的功能,用于检查用户完成滑块验证时是否存在异常。具体而言,该方法接收一个滑块验证码的图片轨迹数据和验证信息等参数,通过计算出用户实际点击位置与给定位置之间的误差,并根据设置的容错范围判断该验证码是否通过验证。
具体流程如下:
需要注意的是,这段代码仅提供了部分验证功能,并不能保证完全有效。因此,在实现安全性要求更高的验证码时,还需要考虑其他安全机制以防止恶意攻击
当然你也可以自己实现ImageCaptchaValidator
类, 做自己定制化校验规则.
至此验证码生成,验证逻辑就梳理完了.
由于本人才疏学浅, 文章难免有错误的地方, 欢迎指正~