diff --git a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaInfo.java b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaInfo.java index 992234d..612a12a 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaInfo.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/SliderCaptchaInfo.java @@ -16,10 +16,6 @@ public class SliderCaptchaInfo { * x轴 */ private Integer x; - /** - * x轴百分比 - */ - private Float xPercent; /** * y轴 */ @@ -32,21 +28,39 @@ public class SliderCaptchaInfo { * 移动图 */ private String sliderImage; + /** 背景图片宽度. */ + private Integer bgImageWidth; + /** 背景图片高度. */ + private Integer bgImageHeight; + /** 滑块图片宽度. */ + private Integer sliderImageWidth; + /** 滑块图片高度. */ + private Integer sliderImageHeight; /** * 扩展字段 */ public Object expand; - public SliderCaptchaInfo(Integer x, Float xPercent, Integer y, String backgroundImage, String sliderImage) { + public SliderCaptchaInfo(Integer x, Integer y, String backgroundImage, String sliderImage, Integer bgImageWidth, Integer bgImageHeight, Integer sliderImageWidth, Integer sliderImageHeight) { this.x = x; - this.xPercent = xPercent; this.y = y; this.backgroundImage = backgroundImage; this.sliderImage = sliderImage; + this.bgImageWidth = bgImageWidth; + this.bgImageHeight = bgImageHeight; + this.sliderImageWidth = sliderImageWidth; + this.sliderImageHeight = sliderImageHeight; } - public static SliderCaptchaInfo of(Integer x, Float xPercent, Integer y, String backgroundImage, String sliderImage) { - return new SliderCaptchaInfo(x, xPercent, y, backgroundImage, sliderImage); + public static SliderCaptchaInfo of(Integer x, + Integer y, + String backgroundImage, + String sliderImage, + Integer bgImageWidth, + Integer bgImageHeight, + Integer sliderImageWidth, + Integer sliderImageHeight) { + return new SliderCaptchaInfo(x, y, backgroundImage, sliderImage, bgImageWidth, bgImageHeight, sliderImageWidth, sliderImageHeight); } } diff --git a/src/main/java/cloud/tianai/captcha/template/slider/StandardSliderCaptchaTemplate.java b/src/main/java/cloud/tianai/captcha/template/slider/StandardSliderCaptchaTemplate.java index 0654469..5a98ed5 100644 --- a/src/main/java/cloud/tianai/captcha/template/slider/StandardSliderCaptchaTemplate.java +++ b/src/main/java/cloud/tianai/captcha/template/slider/StandardSliderCaptchaTemplate.java @@ -151,19 +151,21 @@ public class StandardSliderCaptchaTemplate implements SliderCaptchaTemplate { GenerateParam param) { String backgroundFormatName = param.getBackgroundFormatName(); String sliderFormatName = param.getSliderFormatName(); - // 计算滑块百分比 - float xPercent = (float) randomX / backgroundImage.getWidth(); String backGroundImageBase64 = transform(backgroundImage, backgroundFormatName); String sliderImageBase64 = transform(sliderImage, sliderFormatName); - return SliderCaptchaInfo.of(randomX, xPercent, randomY, + return SliderCaptchaInfo.of(randomX, randomY, backGroundImageBase64, - sliderImageBase64); + sliderImageBase64, + backgroundImage.getWidth(), backgroundImage.getHeight(), + sliderImage.getWidth(), sliderImage.getHeight() + ); } /** * 将图片转换成字符串格式 + * * @param bufferedImage 图片 - * @param formatType 格式化类型 + * @param formatType 格式化类型 * @return String * @throws IOException */ diff --git a/src/main/java/cloud/tianai/captcha/template/slider/util/CollectionUtils.java b/src/main/java/cloud/tianai/captcha/template/slider/util/CollectionUtils.java new file mode 100644 index 0000000..4005af9 --- /dev/null +++ b/src/main/java/cloud/tianai/captcha/template/slider/util/CollectionUtils.java @@ -0,0 +1,393 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cloud.tianai.captcha.template.slider.util; + +import java.util.*; + +/** + * + * 拷贝spring + * Miscellaneous collection utility methods. + * Mainly for internal use within the framework. + * + * @author Juergen Hoeller + * @author Rob Harrop + * @author Arjen Poutsma + * @since 1.1.3 + */ +public abstract class CollectionUtils { + + /** + * Return {@code true} if the supplied Collection is {@code null} or empty. + * Otherwise, return {@code false}. + * @param collection the Collection to check + * @return whether the given Collection is empty + */ + public static boolean isEmpty(Collection> collection) { + return (collection == null || collection.isEmpty()); + } + + /** + * Return {@code true} if the supplied Map is {@code null} or empty. + * Otherwise, return {@code false}. + * @param map the Map to check + * @return whether the given Map is empty + */ + public static boolean isEmpty(Map, ?> map) { + return (map == null || map.isEmpty()); + } + + + + /** + * Merge the given Properties instance into the given Map, + * copying all properties (key-value pairs) over. + *
Uses {@code Properties.propertyNames()} to even catch
+ * default properties linked into the original Properties instance.
+ * @param props the Properties instance to merge (may be {@code null})
+ * @param map the target Map to merge the properties into
+ */
+ @SuppressWarnings("unchecked")
+ public static Enforces the given instance to be present, rather than returning
+ * {@code true} for an equal element as well.
+ * @param collection the Collection to check
+ * @param element the element to look for
+ * @return {@code true} if found, {@code false} otherwise
+ */
+ public static boolean containsInstance(Collection> collection, Object element) {
+ if (collection != null) {
+ for (Object candidate : collection) {
+ if (candidate == element) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return {@code true} if any element in '{@code candidates}' is
+ * contained in '{@code source}'; otherwise returns {@code false}.
+ * @param source the source Collection
+ * @param candidates the candidates to search for
+ * @return whether any of the candidates has been found
+ */
+ public static boolean containsAny(Collection> source, Collection> candidates) {
+ return findFirstMatch(source, candidates) != null;
+ }
+
+ /**
+ * Return the first element in '{@code candidates}' that is contained in
+ * '{@code source}'. If no element in '{@code candidates}' is present in
+ * '{@code source}' returns {@code null}. Iteration order is
+ * {@link Collection} implementation specific.
+ * @param source the source Collection
+ * @param candidates the candidates to search for
+ * @return the first present object, or {@code null} if not found
+ */
+ @SuppressWarnings("unchecked")
+ public static Mainly for internal use within the framework.
+ *
+ * Thanks to Alex Ruiz for contributing several enhancements to this class!
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @since 19.03.2004
+ * @see CollectionUtils
+ */
+public abstract class ObjectUtils {
+
+ private static final int INITIAL_HASH = 7;
+ private static final int MULTIPLIER = 31;
+
+ private static final String EMPTY_STRING = "";
+ private static final String NULL_STRING = "null";
+ private static final String ARRAY_START = "{";
+ private static final String ARRAY_END = "}";
+ private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
+ private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+
+ /**
+ * Return whether the given throwable is a checked exception:
+ * that is, neither a RuntimeException nor an Error.
+ * @param ex the throwable to check
+ * @return whether the throwable is a checked exception
+ * @see java.lang.Exception
+ * @see java.lang.RuntimeException
+ * @see java.lang.Error
+ */
+ public static boolean isCheckedException(Throwable ex) {
+ return !(ex instanceof RuntimeException || ex instanceof Error);
+ }
+
+ /**
+ * Check whether the given exception is compatible with the specified
+ * exception types, as declared in a throws clause.
+ * @param ex the exception to check
+ * @param declaredExceptions the exception types declared in the throws clause
+ * @return whether the given exception is compatible
+ */
+ public static boolean isCompatibleWithThrowsClause(Throwable ex, Class>... declaredExceptions) {
+ if (!isCheckedException(ex)) {
+ return true;
+ }
+ if (declaredExceptions != null) {
+ for (Class> declaredException : declaredExceptions) {
+ if (declaredException.isInstance(ex)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the given object is an array:
+ * either an Object array or a primitive array.
+ * @param obj the object to check
+ */
+ public static boolean isArray(Object obj) {
+ return (obj != null && obj.getClass().isArray());
+ }
+
+ /**
+ * Determine whether the given array is empty:
+ * i.e. {@code null} or of zero length.
+ * @param array the array to check
+ * @see #isEmpty(Object)
+ */
+ public static boolean isEmpty(Object[] array) {
+ return (array == null || array.length == 0);
+ }
+
+ /**
+ * Determine whether the given object is empty.
+ * This method supports the following object types.
+ * If the given object is non-null and not one of the aforementioned
+ * supported types, this method returns {@code false}.
+ * @param obj the object to check
+ * @return {@code true} if the object is {@code null} or empty
+ * @since 4.2
+ * @see Optional#isPresent()
+ * @see ObjectUtils#isEmpty(Object[])
+ * @see CollectionUtils#isEmpty(java.util.Collection)
+ * @see CollectionUtils#isEmpty(java.util.Map)
+ */
+ @SuppressWarnings("rawtypes")
+ public static boolean isEmpty(Object obj) {
+ if (obj == null) {
+ return true;
+ }
+
+ if (obj instanceof Optional) {
+ return !((Optional) obj).isPresent();
+ }
+ if (obj instanceof CharSequence) {
+ return ((CharSequence) obj).length() == 0;
+ }
+ if (obj.getClass().isArray()) {
+ return Array.getLength(obj) == 0;
+ }
+ if (obj instanceof Collection) {
+ return ((Collection) obj).isEmpty();
+ }
+ if (obj instanceof Map) {
+ return ((Map) obj).isEmpty();
+ }
+
+ // else
+ return false;
+ }
+
+ /**
+ * Unwrap the given object which is potentially a {@link java.util.Optional}.
+ * @param obj the candidate object
+ * @return either the value held within the {@code Optional}, {@code null}
+ * if the {@code Optional} is empty, or simply the given object as-is
+ * @since 5.0
+ */
+ public static Object unwrapOptional(Object obj) {
+ if (obj instanceof Optional) {
+ Optional> optional = (Optional>) obj;
+ if (!optional.isPresent()) {
+ return null;
+ }
+ Object result = optional.get();
+ return result;
+ }
+ return obj;
+ }
+
+ /**
+ * Check whether the given array contains the given element.
+ * @param array the array to check (may be {@code null},
+ * in which case the return value will always be {@code false})
+ * @param element the element to check for
+ * @return whether the element has been found in the given array
+ */
+ public static boolean containsElement(Object[] array, Object element) {
+ if (array == null) {
+ return false;
+ }
+ for (Object arrayEle : array) {
+ if (nullSafeEquals(arrayEle, element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the given array of enum constants contains a constant with the given name,
+ * ignoring case when determining a match.
+ * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
+ * @param constant the constant name to find (must not be null or empty string)
+ * @return whether the constant has been found in the given array
+ */
+ public static boolean containsConstant(Enum>[] enumValues, String constant) {
+ return containsConstant(enumValues, constant, false);
+ }
+
+ /**
+ * Check whether the given array of enum constants contains a constant with the given name.
+ * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
+ * @param constant the constant name to find (must not be null or empty string)
+ * @param caseSensitive whether case is significant in determining a match
+ * @return whether the constant has been found in the given array
+ */
+ public static boolean containsConstant(Enum>[] enumValues, String constant, boolean caseSensitive) {
+ for (Enum> candidate : enumValues) {
+ if (caseSensitive ? candidate.toString().equals(constant) :
+ candidate.toString().equalsIgnoreCase(constant)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
+ * @param A {@code null} source value will be converted to an
+ * empty Object array.
+ * @param source the (potentially primitive) array
+ * @return the corresponding object array (never {@code null})
+ * @throws IllegalArgumentException if the parameter is not an array
+ */
+ public static Object[] toObjectArray(Object source) {
+ if (source instanceof Object[]) {
+ return (Object[]) source;
+ }
+ if (source == null) {
+ return EMPTY_OBJECT_ARRAY;
+ }
+ if (!source.getClass().isArray()) {
+ throw new IllegalArgumentException("Source is not an array: " + source);
+ }
+ int length = Array.getLength(source);
+ if (length == 0) {
+ return EMPTY_OBJECT_ARRAY;
+ }
+ Class> wrapperType = Array.get(source, 0).getClass();
+ Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
+ for (int i = 0; i < length; i++) {
+ newArray[i] = Array.get(source, i);
+ }
+ return newArray;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for content-based equality/hash-code handling
+ //---------------------------------------------------------------------
+
+ /**
+ * Determine if the given objects are equal, returning {@code true} if
+ * both are {@code null} or {@code false} if only one is {@code null}.
+ * Compares arrays with {@code Arrays.equals}, performing an equality
+ * check based on the array elements rather than the array reference.
+ * @param o1 first Object to compare
+ * @param o2 second Object to compare
+ * @return whether the given objects are equal
+ * @see Object#equals(Object)
+ * @see java.util.Arrays#equals
+ */
+ public static boolean nullSafeEquals(Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ }
+ if (o1 == null || o2 == null) {
+ return false;
+ }
+ if (o1.equals(o2)) {
+ return true;
+ }
+ if (o1.getClass().isArray() && o2.getClass().isArray()) {
+ return arrayEquals(o1, o2);
+ }
+ return false;
+ }
+
+ /**
+ * Compare the given arrays with {@code Arrays.equals}, performing an equality
+ * check based on the array elements rather than the array reference.
+ * @param o1 first array to compare
+ * @param o2 second array to compare
+ * @return whether the given objects are equal
+ * @see #nullSafeEquals(Object, Object)
+ * @see java.util.Arrays#equals
+ */
+ private static boolean arrayEquals(Object o1, Object o2) {
+ if (o1 instanceof Object[] && o2 instanceof Object[]) {
+ return Arrays.equals((Object[]) o1, (Object[]) o2);
+ }
+ if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
+ return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+ }
+ if (o1 instanceof byte[] && o2 instanceof byte[]) {
+ return Arrays.equals((byte[]) o1, (byte[]) o2);
+ }
+ if (o1 instanceof char[] && o2 instanceof char[]) {
+ return Arrays.equals((char[]) o1, (char[]) o2);
+ }
+ if (o1 instanceof double[] && o2 instanceof double[]) {
+ return Arrays.equals((double[]) o1, (double[]) o2);
+ }
+ if (o1 instanceof float[] && o2 instanceof float[]) {
+ return Arrays.equals((float[]) o1, (float[]) o2);
+ }
+ if (o1 instanceof int[] && o2 instanceof int[]) {
+ return Arrays.equals((int[]) o1, (int[]) o2);
+ }
+ if (o1 instanceof long[] && o2 instanceof long[]) {
+ return Arrays.equals((long[]) o1, (long[]) o2);
+ }
+ if (o1 instanceof short[] && o2 instanceof short[]) {
+ return Arrays.equals((short[]) o1, (short[]) o2);
+ }
+ return false;
+ }
+
+ /**
+ * Return as hash code for the given object; typically the value of
+ * {@code Object#hashCode()}}. If the object is an array,
+ * this method will delegate to any of the {@code nullSafeHashCode}
+ * methods for arrays in this class. If the object is {@code null},
+ * this method returns 0.
+ * @see Object#hashCode()
+ * @see #nullSafeHashCode(Object[])
+ * @see #nullSafeHashCode(boolean[])
+ * @see #nullSafeHashCode(byte[])
+ * @see #nullSafeHashCode(char[])
+ * @see #nullSafeHashCode(double[])
+ * @see #nullSafeHashCode(float[])
+ * @see #nullSafeHashCode(int[])
+ * @see #nullSafeHashCode(long[])
+ * @see #nullSafeHashCode(short[])
+ */
+ public static int nullSafeHashCode(Object obj) {
+ if (obj == null) {
+ return 0;
+ }
+ if (obj.getClass().isArray()) {
+ if (obj instanceof Object[]) {
+ return nullSafeHashCode((Object[]) obj);
+ }
+ if (obj instanceof boolean[]) {
+ return nullSafeHashCode((boolean[]) obj);
+ }
+ if (obj instanceof byte[]) {
+ return nullSafeHashCode((byte[]) obj);
+ }
+ if (obj instanceof char[]) {
+ return nullSafeHashCode((char[]) obj);
+ }
+ if (obj instanceof double[]) {
+ return nullSafeHashCode((double[]) obj);
+ }
+ if (obj instanceof float[]) {
+ return nullSafeHashCode((float[]) obj);
+ }
+ if (obj instanceof int[]) {
+ return nullSafeHashCode((int[]) obj);
+ }
+ if (obj instanceof long[]) {
+ return nullSafeHashCode((long[]) obj);
+ }
+ if (obj instanceof short[]) {
+ return nullSafeHashCode((short[]) obj);
+ }
+ }
+ return obj.hashCode();
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(Object[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (Object element : array) {
+ hash = MULTIPLIER * hash + nullSafeHashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(boolean[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (boolean element : array) {
+ hash = MULTIPLIER * hash + Boolean.hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(byte[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (byte element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(char[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (char element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(double[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (double element : array) {
+ hash = MULTIPLIER * hash + Double.hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(float[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (float element : array) {
+ hash = MULTIPLIER * hash + Float.hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(int[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (int element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(long[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (long element : array) {
+ hash = MULTIPLIER * hash + Long.hashCode(element);
+ }
+ return hash;
+ }
+
+ /**
+ * Return a hash code based on the contents of the specified array.
+ * If {@code array} is {@code null}, this method returns 0.
+ */
+ public static int nullSafeHashCode(short[] array) {
+ if (array == null) {
+ return 0;
+ }
+ int hash = INITIAL_HASH;
+ for (short element : array) {
+ hash = MULTIPLIER * hash + element;
+ }
+ return hash;
+ }
+
+ /**
+ * Return the same value as {@link Boolean#hashCode(boolean)}}.
+ * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
+ */
+ @Deprecated
+ public static int hashCode(boolean bool) {
+ return Boolean.hashCode(bool);
+ }
+
+ /**
+ * Return the same value as {@link Double#hashCode(double)}}.
+ * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
+ */
+ @Deprecated
+ public static int hashCode(double dbl) {
+ return Double.hashCode(dbl);
+ }
+
+ /**
+ * Return the same value as {@link Float#hashCode(float)}}.
+ * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
+ */
+ @Deprecated
+ public static int hashCode(float flt) {
+ return Float.hashCode(flt);
+ }
+
+ /**
+ * Return the same value as {@link Long#hashCode(long)}}.
+ * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant
+ */
+ @Deprecated
+ public static int hashCode(long lng) {
+ return Long.hashCode(lng);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Convenience methods for toString output
+ //---------------------------------------------------------------------
+
+ /**
+ * Return a String representation of an object's overall identity.
+ * @param obj the object (may be {@code null})
+ * @return the object's identity as String representation,
+ * or an empty String if the object was {@code null}
+ */
+ public static String identityToString(Object obj) {
+ if (obj == null) {
+ return EMPTY_STRING;
+ }
+ String className = obj.getClass().getName();
+ String identityHexString = getIdentityHexString(obj);
+ return className + '@' + identityHexString;
+ }
+
+ /**
+ * Return a hex String form of an object's identity hash code.
+ * @param obj the object
+ * @return the object's identity code in hex notation
+ */
+ public static String getIdentityHexString(Object obj) {
+ return Integer.toHexString(System.identityHashCode(obj));
+ }
+
+ /**
+ * Return a content-based String representation if {@code obj} is
+ * not {@code null}; otherwise returns an empty String.
+ * Differs from {@link #nullSafeToString(Object)} in that it returns
+ * an empty String rather than "null" for a {@code null} value.
+ * @param obj the object to build a display String for
+ * @return a display String representation of {@code obj}
+ * @see #nullSafeToString(Object)
+ */
+ public static String getDisplayString(Object obj) {
+ if (obj == null) {
+ return EMPTY_STRING;
+ }
+ return nullSafeToString(obj);
+ }
+
+ /**
+ * Determine the class name for the given object.
+ * Returns a {@code "null"} String if {@code obj} is {@code null}.
+ * @param obj the object to introspect (may be {@code null})
+ * @return the corresponding class name
+ */
+ public static String nullSafeClassName(Object obj) {
+ return (obj != null ? obj.getClass().getName() : NULL_STRING);
+ }
+
+ /**
+ * Return a String representation of the specified Object.
+ * Builds a String representation of the contents in case of an array.
+ * Returns a {@code "null"} String if {@code obj} is {@code null}.
+ * @param obj the object to build a String representation for
+ * @return a String representation of {@code obj}
+ */
+ public static String nullSafeToString(Object obj) {
+ if (obj == null) {
+ return NULL_STRING;
+ }
+ if (obj instanceof String) {
+ return (String) obj;
+ }
+ if (obj instanceof Object[]) {
+ return nullSafeToString((Object[]) obj);
+ }
+ if (obj instanceof boolean[]) {
+ return nullSafeToString((boolean[]) obj);
+ }
+ if (obj instanceof byte[]) {
+ return nullSafeToString((byte[]) obj);
+ }
+ if (obj instanceof char[]) {
+ return nullSafeToString((char[]) obj);
+ }
+ if (obj instanceof double[]) {
+ return nullSafeToString((double[]) obj);
+ }
+ if (obj instanceof float[]) {
+ return nullSafeToString((float[]) obj);
+ }
+ if (obj instanceof int[]) {
+ return nullSafeToString((int[]) obj);
+ }
+ if (obj instanceof long[]) {
+ return nullSafeToString((long[]) obj);
+ }
+ if (obj instanceof short[]) {
+ return nullSafeToString((short[]) obj);
+ }
+ String str = obj.toString();
+ return (str != null ? str : EMPTY_STRING);
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(Object[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (Object o : array) {
+ stringJoiner.add(String.valueOf(o));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(boolean[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (boolean b : array) {
+ stringJoiner.add(String.valueOf(b));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(byte[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (byte b : array) {
+ stringJoiner.add(String.valueOf(b));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(char[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (char c : array) {
+ stringJoiner.add('\'' + String.valueOf(c) + '\'');
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(double[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (double d : array) {
+ stringJoiner.add(String.valueOf(d));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(float[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (float f : array) {
+ stringJoiner.add(String.valueOf(f));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(int[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (int i : array) {
+ stringJoiner.add(String.valueOf(i));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(long[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (long l : array) {
+ stringJoiner.add(String.valueOf(l));
+ }
+ return stringJoiner.toString();
+ }
+
+ /**
+ * Return a String representation of the contents of the specified array.
+ * The String representation consists of a list of the array's elements,
+ * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
+ * by the characters {@code ", "} (a comma followed by a space).
+ * Returns a {@code "null"} String if {@code array} is {@code null}.
+ * @param array the array to build a String representation for
+ * @return a String representation of {@code array}
+ */
+ public static String nullSafeToString(short[] array) {
+ if (array == null) {
+ return NULL_STRING;
+ }
+ int length = array.length;
+ if (length == 0) {
+ return EMPTY_ARRAY;
+ }
+ StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END);
+ for (short s : array) {
+ stringJoiner.add(String.valueOf(s));
+ }
+ return stringJoiner.toString();
+ }
+
+}
diff --git a/src/main/java/cloud/tianai/captcha/template/slider/validator/BasicCaptchaTrackValidator.java b/src/main/java/cloud/tianai/captcha/template/slider/validator/BasicCaptchaTrackValidator.java
new file mode 100644
index 0000000..e9d3dc1
--- /dev/null
+++ b/src/main/java/cloud/tianai/captcha/template/slider/validator/BasicCaptchaTrackValidator.java
@@ -0,0 +1,126 @@
+package cloud.tianai.captcha.template.slider.validator;
+
+import cloud.tianai.captcha.template.slider.util.CollectionUtils;
+import cloud.tianai.captcha.template.slider.util.ObjectUtils;
+
+import java.util.List;
+
+/**
+ * @Author: 天爱有情
+ * @date 2022/2/17 11:01
+ * @Description 基本的行为轨迹校验
+ */
+public class BasicCaptchaTrackValidator extends SimpleSliderCaptchaValidator {
+
+ public BasicCaptchaTrackValidator() {
+ }
+
+ public BasicCaptchaTrackValidator(float defaultTolerant) {
+ super(defaultTolerant);
+ }
+
+ @Override
+ public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage) {
+ // 校验参数
+ checkParam(sliderCaptchaTrack);
+ // 基础校验
+ boolean superValid = super.valid(sliderCaptchaTrack, oriPercentage);
+ if (!superValid) {
+ return false;
+ }
+ // 进行行为轨迹检测
+ long startSlidingTime = sliderCaptchaTrack.getStartSlidingTime().getTime();
+ long endSlidingTime = sliderCaptchaTrack.getEntSlidingTime().getTime();
+ Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
+ List
+ *
+ *