mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-07 06:04:34 +08:00
重构校验器, 升级版本为1.4.1
This commit is contained in:
@@ -11,10 +11,19 @@ public interface CaptchaTypeConstant {
|
||||
String SLIDER = "SLIDER";
|
||||
/** 旋转. */
|
||||
String ROTATE = "ROTATE";
|
||||
/** 旋转角度.*/
|
||||
String ROTATE_DEGREE = "ROTATE_DEGREE";
|
||||
/** 拼接.*/
|
||||
String CONCAT = "CONCAT";
|
||||
/** 拼图.*/
|
||||
String JIGSAW = "JIGSAW";
|
||||
/** 图片点选.*/
|
||||
String IMAGE_CLICK = "IMAGE_CLICK";
|
||||
/** 文字图片点选.*/
|
||||
String WORD_IMAGE_CLICK = "WORD_IMAGE_CLICK";
|
||||
/** 语序点选.*/
|
||||
String WORD_ORDER_IMAGE_CLICK = "WORD_ORDER_IMAGE_CLICK";
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package cloud.tianai.captcha.common.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/4/20 9:53
|
||||
* @Description 可能是最好用的API统一返回格式类
|
||||
*/
|
||||
@Data
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class ApiResponse<T> implements Serializable {
|
||||
|
||||
public static final ApiResponse<?> SUCCESS;
|
||||
|
||||
static {
|
||||
CodeDefinition definition = ApiResponseStatusConstant.SUCCESS;
|
||||
SUCCESS = new ApiResponse(definition.getCode(), definition.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* code码.
|
||||
*/
|
||||
private Integer code;
|
||||
/**
|
||||
* 信息.
|
||||
*/
|
||||
private String msg;
|
||||
/**
|
||||
* 成功时返回的数据.
|
||||
*/
|
||||
private T data;
|
||||
|
||||
public ApiResponse(Integer code, String errMsg, T data) {
|
||||
this.code = code;
|
||||
this.msg = errMsg;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public ApiResponse(CodeDefinition definition, T data) {
|
||||
this.code = definition.getCode();
|
||||
this.msg = definition.getMessage();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public ApiResponse() {
|
||||
CodeDefinition definition = ApiResponseStatusConstant.SUCCESS;
|
||||
this.code = definition.getCode();
|
||||
this.msg = definition.getMessage();
|
||||
}
|
||||
|
||||
public <R> ApiResponse<R> convert() {
|
||||
ApiResponse<R> result = new ApiResponse<>();
|
||||
result.setCode(this.getCode());
|
||||
result.setMsg(this.getMsg());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSuccess() {
|
||||
return ApiResponseStatusConstant.SUCCESS.getCode().equals(getCode());
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> of(Integer code, String msg, T data) {
|
||||
return new ApiResponse(code, msg, data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> of(CodeDefinition definition, T data) {
|
||||
return new ApiResponse(definition.getCode(), definition.getMessage(), data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofMessage(CodeDefinition definition) {
|
||||
return new ApiResponse(definition.getCode(), definition.getMessage(), null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofError(String message) {
|
||||
return new ApiResponse(ApiResponseStatusConstant.INTERNAL_SERVER_ERROR.getCode(), message, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofError(String message, Object obj) {
|
||||
return new ApiResponse(ApiResponseStatusConstant.INTERNAL_SERVER_ERROR.getCode(), message, obj);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofCheckError(String message) {
|
||||
return new ApiResponse(ApiResponseStatusConstant.NOT_VALID_PARAM.getCode(), message, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofSuccess(T data) {
|
||||
CodeDefinition definition = ApiResponseStatusConstant.SUCCESS;
|
||||
return new ApiResponse(definition.getCode(), definition.getMessage(), data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> ofSuccess() {
|
||||
return (ApiResponse<T>) SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cloud.tianai.captcha.common.response;
|
||||
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @Date 2020/5/26 17:58
|
||||
* @Description 统一返回错误码, 详见 阿里巴巴开发规范 错误码列表
|
||||
* <p>
|
||||
* 该枚举定义了一些公共的code码,自定义code码数据需在自己业务中编写
|
||||
*/
|
||||
public interface ApiResponseStatusConstant {
|
||||
|
||||
/**
|
||||
* 成功.
|
||||
*/
|
||||
CodeDefinition SUCCESS = new CodeDefinition(200, "OK");
|
||||
|
||||
CodeDefinition NOT_VALID_PARAM = new CodeDefinition(403, "无效参数");
|
||||
|
||||
CodeDefinition INTERNAL_SERVER_ERROR = new CodeDefinition(500, "未知的内部错误");
|
||||
|
||||
CodeDefinition EXPIRED = new CodeDefinition(4000, "已失效");
|
||||
|
||||
CodeDefinition BASIC_CHECK_FAIL = new CodeDefinition(4001, "基础校验失败");
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package cloud.tianai.captcha.common.response;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2022/4/13 12:37
|
||||
* @Description code 定义
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CodeDefinition {
|
||||
|
||||
private Integer code;
|
||||
private String message;
|
||||
}
|
||||
@@ -32,4 +32,13 @@ public class CaptchaUtils {
|
||||
return CaptchaTypeConstant.WORD_IMAGE_CLICK.equals(type) || CaptchaTypeConstant.IMAGE_CLICK.equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是拼图类验证码, 校验的值是 数字排列顺序
|
||||
*
|
||||
* @param type 类型
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isJigsawCaptcha(String type) {
|
||||
return CaptchaTypeConstant.JIGSAW.equals(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cloud.tianai.captcha.validator;
|
||||
|
||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
|
||||
|
||||
@@ -12,34 +13,6 @@ import java.util.Map;
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 用于生成验证码校验时需要的回传参数
|
||||
*
|
||||
@@ -52,8 +25,8 @@ public interface ImageCaptchaValidator {
|
||||
* 校验用户滑动滑块是否正确
|
||||
*
|
||||
* @param imageCaptchaTrack 包含了滑动轨迹,展示的图片宽高,滑动时间等参数
|
||||
* @param sliderCaptchaValidData generateSliderCaptchaValidData(生成的数据
|
||||
* @return boolean
|
||||
* @param imageCaptchaValidData generateImageCaptchaValidData(生成的数据)
|
||||
* @return ApiResponse<?>
|
||||
*/
|
||||
boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData);
|
||||
ApiResponse<?> valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> imageCaptchaValidData);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package cloud.tianai.captcha.validator;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/1/19 10:40
|
||||
* @Description 滑动类验证码百分比校验
|
||||
*/
|
||||
public interface SliderCaptchaPercentageValidator {
|
||||
|
||||
/**
|
||||
* 计算滑块要背景图的百分比,基本校验
|
||||
* 用于计算滑动类验证码的缺口位置
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package cloud.tianai.captcha.validator.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||
import cloud.tianai.captcha.common.response.CodeDefinition;
|
||||
import cloud.tianai.captcha.common.util.CaptchaUtils;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||
@@ -18,7 +20,7 @@ import java.util.Map;
|
||||
* @Description 基本的行为轨迹校验
|
||||
*/
|
||||
public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
|
||||
|
||||
public static final CodeDefinition DEFINITION = new CodeDefinition(50001, "basic check fail");
|
||||
public BasicCaptchaTrackValidator() {
|
||||
}
|
||||
|
||||
@@ -27,17 +29,17 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
public ApiResponse<?> beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
// 校验参数
|
||||
checkParam(imageCaptchaTrack);
|
||||
return true;
|
||||
return ApiResponse.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
if (!CaptchaUtils.isSliderCaptcha(type)){
|
||||
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
if (!CaptchaUtils.isSliderCaptcha(type)) {
|
||||
// 不是滑动验证码的话暂时跳过,点选验证码行为轨迹还没做
|
||||
return true;
|
||||
return ApiResponse.ofSuccess();
|
||||
}
|
||||
// 进行行为轨迹检测
|
||||
long startSlidingTime = imageCaptchaTrack.getStartSlidingTime().getTime();
|
||||
@@ -55,23 +57,23 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
|
||||
|
||||
// 检测1
|
||||
if (startSlidingTime + 300 > endSlidingTime) {
|
||||
return false;
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
// 检测2
|
||||
if (trackList.size() < 10 || trackList.size() > bgImageWidth * 5) {
|
||||
return false;
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
// 检测3
|
||||
ImageCaptchaTrack.Track firstTrack = trackList.get(0);
|
||||
if (firstTrack.getX() > 10 || firstTrack.getX() < -10 || firstTrack.getY() > 10 || firstTrack.getY() < -10) {
|
||||
return false;
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
int check4 = 0;
|
||||
int check7 = 0;
|
||||
for (int i = 1; i < trackList.size(); i++) {
|
||||
ImageCaptchaTrack.Track track = trackList.get(i);
|
||||
int x = track.getX();
|
||||
int y = track.getY();
|
||||
float x = track.getX();
|
||||
float y = track.getY();
|
||||
// check4
|
||||
if (firstTrack.getY() == y) {
|
||||
check4++;
|
||||
@@ -83,23 +85,27 @@ public class BasicCaptchaTrackValidator extends SimpleImageCaptchaValidator {
|
||||
// check5
|
||||
ImageCaptchaTrack.Track preTrack = trackList.get(i - 1);
|
||||
if ((track.getX() - preTrack.getX()) > 50 || (track.getY() - preTrack.getY()) > 50) {
|
||||
return false;
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
}
|
||||
if (check4 == trackList.size() || check7 > 200) {
|
||||
return false;
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
|
||||
// check6
|
||||
int splitPos = (int) (trackList.size() * 0.7);
|
||||
ImageCaptchaTrack.Track splitPostTrack = trackList.get(splitPos - 1);
|
||||
int posTime = splitPostTrack.getT();
|
||||
float posTime = splitPostTrack.getT();
|
||||
float startAvgPosTime = posTime / (float) splitPos;
|
||||
|
||||
ImageCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
|
||||
float endAvgPosTime = lastTrack.getT() / (float) (trackList.size() - splitPos);
|
||||
double endAvgPosTime = lastTrack.getT() / (float) (trackList.size() - splitPos);
|
||||
|
||||
return endAvgPosTime > startAvgPosTime;
|
||||
boolean check = endAvgPosTime > startAvgPosTime;
|
||||
if (check) {
|
||||
return ApiResponse.ofSuccess();
|
||||
}
|
||||
return ApiResponse.ofMessage(DEFINITION);
|
||||
}
|
||||
|
||||
public void checkParam(ImageCaptchaTrack imageCaptchaTrack) {
|
||||
|
||||
+73
-43
@@ -1,18 +1,22 @@
|
||||
package cloud.tianai.captcha.validator.impl;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CaptchaTypeConstant;
|
||||
import cloud.tianai.captcha.common.response.ApiResponse;
|
||||
import cloud.tianai.captcha.common.response.ApiResponseStatusConstant;
|
||||
import cloud.tianai.captcha.common.util.CaptchaUtils;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.common.util.ObjectUtils;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ClickImageCheckDefinition;
|
||||
import cloud.tianai.captcha.generator.common.model.dto.ImageCaptchaInfo;
|
||||
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
|
||||
import cloud.tianai.captcha.validator.SliderCaptchaPercentageValidator;
|
||||
import cloud.tianai.captcha.validator.common.constant.TrackTypeConstant;
|
||||
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -24,7 +28,7 @@ import java.util.stream.Collectors;
|
||||
* @Description 基本的滑块验证校验 , 值进行基本校验, 目前只校验用户是否滑动到缺口处,不校验行为轨迹
|
||||
*/
|
||||
@Slf4j
|
||||
public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
public class SimpleImageCaptchaValidator implements ImageCaptchaValidator, SliderCaptchaPercentageValidator {
|
||||
|
||||
/** 默认的容错值. */
|
||||
public static float DEFAULT_TOLERANT = 0.02f;
|
||||
@@ -34,7 +38,6 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
public static final String TOLERANT_KEY = "tolerant";
|
||||
/** 类型 key, 标识是哪张类型的验证码. */
|
||||
public static final String TYPE_KEY = "type";
|
||||
|
||||
/** 容错值. */
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -136,95 +139,115 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
}
|
||||
// 添加点选验证数据
|
||||
map.put(PERCENTAGE_KEY, sb.toString());
|
||||
} else if (CaptchaUtils.isJigsawCaptcha(type)) {
|
||||
// 拼图验证码
|
||||
map.put(PERCENTAGE_KEY, expand);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData) {
|
||||
public ApiResponse<?> valid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> imageCaptchaValidData) {
|
||||
// 读容错值
|
||||
Float tolerant = getFloatParam(TOLERANT_KEY, sliderCaptchaValidData, defaultTolerant);
|
||||
Float tolerant = getFloatParam(TOLERANT_KEY, imageCaptchaValidData, defaultTolerant);
|
||||
// 读验证码类型
|
||||
String type = getStringParam(TYPE_KEY, sliderCaptchaValidData, CaptchaTypeConstant.SLIDER);
|
||||
String type = getStringParam(TYPE_KEY, imageCaptchaValidData, CaptchaTypeConstant.SLIDER);
|
||||
// 验证前
|
||||
// 在验证前必须读取 容错值 和验证码类型
|
||||
if (!beforeValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type)) {
|
||||
return false;
|
||||
ApiResponse<?> beforeValid = beforeValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
if (!beforeValid.isSuccess()) {
|
||||
return beforeValid;
|
||||
}
|
||||
Integer bgImageWidth = imageCaptchaTrack.getBgImageWidth();
|
||||
if (bgImageWidth == null || bgImageWidth < 1) {
|
||||
// 没有背景图片宽度
|
||||
return false;
|
||||
return ApiResponse.ofCheckError("验证码背景图片宽度参数错误");
|
||||
}
|
||||
List<ImageCaptchaTrack.Track> trackList = imageCaptchaTrack.getTrackList();
|
||||
if (CollectionUtils.isEmpty(trackList)) {
|
||||
// 没有滑动轨迹
|
||||
return false;
|
||||
return ApiResponse.ofCheckError("没有解析到滑动轨迹");
|
||||
}
|
||||
// 验证
|
||||
boolean valid = doValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
|
||||
ApiResponse<?> response;
|
||||
boolean valid = doValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
if (valid) {
|
||||
// 验证后
|
||||
valid = afterValid(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
|
||||
response = afterValid(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
}else {
|
||||
// 缺口位置校验失败
|
||||
response = ApiResponse.ofMessage(ApiResponseStatusConstant.BASIC_CHECK_FAIL);
|
||||
}
|
||||
return valid;
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证前
|
||||
*
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param sliderCaptchaValidData sliderCaptchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param captchaValidData captchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
return true;
|
||||
public ApiResponse<?> beforeValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
|
||||
return ApiResponse.ofSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证后
|
||||
*
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param sliderCaptchaValidData sliderCaptchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param captchaValidData captchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> sliderCaptchaValidData, Float tolerant, String type) {
|
||||
return true;
|
||||
public ApiResponse<?> afterValid(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> captchaValidData, Float tolerant, String type) {
|
||||
return ApiResponse.ofSuccess();
|
||||
}
|
||||
|
||||
public boolean doValid(ImageCaptchaTrack imageCaptchaTrack,
|
||||
Map<String, Object> sliderCaptchaValidData,
|
||||
Map<String, Object> imageCaptchaValidData,
|
||||
Float tolerant,
|
||||
String type) {
|
||||
if (CaptchaUtils.isSliderCaptcha(type)) {
|
||||
// 滑动类型验证码
|
||||
return doValidSliderCaptcha(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
|
||||
return doValidSliderCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
} else if (CaptchaUtils.isClickCaptcha(type)) {
|
||||
// 点选类型验证码
|
||||
return doValidClickCaptcha(imageCaptchaTrack, sliderCaptchaValidData, tolerant, type);
|
||||
return doValidClickCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
} else if (CaptchaUtils.isJigsawCaptcha(type)) {
|
||||
// 拼图类型验证码
|
||||
return doValidJigsawCaptcha(imageCaptchaTrack, imageCaptchaValidData, tolerant, type);
|
||||
}
|
||||
// 不支持的类型
|
||||
log.warn("校验验证码警告, 不支持的验证码类型:{}, 请手动扩展 cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator.doValid 进行校验扩展", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean doValidJigsawCaptcha(ImageCaptchaTrack imageCaptchaTrack, Map<String, Object> imageCaptchaValidData, Float tolerant, String type) {
|
||||
if (imageCaptchaTrack.getData() == null || !(imageCaptchaTrack.getData() instanceof String)) {
|
||||
throw new IllegalArgumentException("拼图验证码必须传data数据,且必须是字符串类型逗号分隔数据");
|
||||
}
|
||||
String posArr = (String) imageCaptchaTrack.getData();
|
||||
String successPosStr = getStringParam(PERCENTAGE_KEY, imageCaptchaValidData, null);
|
||||
return successPosStr.equals(posArr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验点选验证码
|
||||
*
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param sliderCaptchaValidData sliderCaptchaValidData
|
||||
* @param imageCaptchaValidData imageCaptchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean doValidClickCaptcha(ImageCaptchaTrack imageCaptchaTrack,
|
||||
Map<String, Object> sliderCaptchaValidData,
|
||||
Map<String, Object> imageCaptchaValidData,
|
||||
Float tolerant,
|
||||
String type) {
|
||||
String validStr = getStringParam(PERCENTAGE_KEY, sliderCaptchaValidData, null);
|
||||
String validStr = getStringParam(PERCENTAGE_KEY, imageCaptchaValidData, null);
|
||||
if (ObjectUtils.isEmpty(validStr)) {
|
||||
return false;
|
||||
}
|
||||
@@ -241,6 +264,8 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
if (clickTrackList.size() != splitArr.length) {
|
||||
return false;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<Double> percentages = new ArrayList<>();
|
||||
for (int i = 0; i < splitArr.length; i++) {
|
||||
ImageCaptchaTrack.Track track = clickTrackList.get(i);
|
||||
String posStr = splitArr[i];
|
||||
@@ -250,12 +275,17 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
|
||||
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;
|
||||
}
|
||||
if (i > 0) {
|
||||
sb.append("|");
|
||||
}
|
||||
sb.append(calcXPercentage).append(",").append(calcYPercentage);
|
||||
percentages.add((double) ((calcXPercentage - xPercentage) + (calcYPercentage - yPercentage)));
|
||||
}
|
||||
// 存储一下当前计算出来的值
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -263,16 +293,16 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
* 校验滑动验证码
|
||||
*
|
||||
* @param imageCaptchaTrack sliderCaptchaTrack
|
||||
* @param sliderCaptchaValidData sliderCaptchaValidData
|
||||
* @param imageCaptchaValidData imageCaptchaValidData
|
||||
* @param tolerant tolerant
|
||||
* @param type type
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean doValidSliderCaptcha(ImageCaptchaTrack imageCaptchaTrack,
|
||||
Map<String, Object> sliderCaptchaValidData,
|
||||
Map<String, Object> imageCaptchaValidData,
|
||||
Float tolerant,
|
||||
String type) {
|
||||
Float oriPercentage = getFloatParam(PERCENTAGE_KEY, sliderCaptchaValidData);
|
||||
Float oriPercentage = getFloatParam(PERCENTAGE_KEY, imageCaptchaValidData);
|
||||
if (oriPercentage == null) {
|
||||
// 没读取到百分比
|
||||
return false;
|
||||
@@ -286,12 +316,12 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
return checkPercentage(calcPercentage, oriPercentage, tolerant);
|
||||
}
|
||||
|
||||
public Float getFloatParam(String key, Map<String, Object> sliderCaptchaValidData) {
|
||||
return getFloatParam(key, sliderCaptchaValidData, null);
|
||||
public Float getFloatParam(String key, Map<String, Object> imageCaptchaValidData) {
|
||||
return getFloatParam(key, imageCaptchaValidData, null);
|
||||
}
|
||||
|
||||
public Float getFloatParam(String key, Map<String, Object> sliderCaptchaValidData, Float defaultData) {
|
||||
Object data = sliderCaptchaValidData.get(key);
|
||||
public Float getFloatParam(String key, Map<String, Object> imageCaptchaValidData, Float defaultData) {
|
||||
Object data = imageCaptchaValidData.get(key);
|
||||
if (data != null) {
|
||||
if (data instanceof Number) {
|
||||
return ((Number) data).floatValue();
|
||||
@@ -301,18 +331,18 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
return Float.parseFloat((String) data);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("从 sliderCaptchaValidData 读取到的 " + key + "无法转换成float类型, [{}]", data);
|
||||
log.error("从 imageCaptchaValidData 读取到的 " + key + "无法转换成float类型, [{}]", data);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return defaultData;
|
||||
}
|
||||
|
||||
public String getStringParam(String key, Map<String, Object> sliderCaptchaValidData, String defaultData) {
|
||||
if (CollectionUtils.isEmpty(sliderCaptchaValidData)) {
|
||||
public String getStringParam(String key, Map<String, Object> imageCaptchaValidData, String defaultData) {
|
||||
if (CollectionUtils.isEmpty(imageCaptchaValidData)) {
|
||||
return defaultData;
|
||||
}
|
||||
Object data = sliderCaptchaValidData.get(key);
|
||||
Object data = imageCaptchaValidData.get(key);
|
||||
if (data != null) {
|
||||
if (data instanceof String) {
|
||||
return (String) data;
|
||||
@@ -320,7 +350,7 @@ public class SimpleImageCaptchaValidator implements ImageCaptchaValidator {
|
||||
try {
|
||||
return String.valueOf(data);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("从 sliderCaptchaValidData 读取到的 " + key + "无法转换成String类型, [{}]", data);
|
||||
log.error("从 imageCaptchaValidData 读取到的 " + key + "无法转换成String类型, [{}]", data);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user