U 加入行为轨迹校验

This commit is contained in:
天爱有情
2022-02-17 15:03:31 +08:00
parent 167d7b56f1
commit f3ac67686f
8 changed files with 1609 additions and 13 deletions
@@ -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);
}
}
@@ -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
*/
@@ -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.
* <p>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 <K, V> void mergePropertiesIntoMap(Properties props, Map<K, V> map) {
if (props != null) {
for (Enumeration<?> en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
Object value = props.get(key);
if (value == null) {
// Allow for defaults fallback or potentially overridden accessor...
value = props.getProperty(key);
}
map.put((K) key, (V) value);
}
}
}
/**
* Check whether the given Iterator contains the given element.
* @param iterator the Iterator to check
* @param element the element to look for
* @return {@code true} if found, {@code false} otherwise
*/
public static boolean contains(Iterator<?> iterator, Object element) {
if (iterator != null) {
while (iterator.hasNext()) {
Object candidate = iterator.next();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
/**
* Check whether the given Enumeration contains the given element.
* @param enumeration the Enumeration to check
* @param element the element to look for
* @return {@code true} if found, {@code false} otherwise
*/
public static boolean contains(Enumeration<?> enumeration, Object element) {
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
Object candidate = enumeration.nextElement();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
/**
* Check whether the given Collection contains the given element instance.
* <p>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 <E> E findFirstMatch(Collection<?> source, Collection<E> candidates) {
if (isEmpty(source) || isEmpty(candidates)) {
return null;
}
for (Object candidate : candidates) {
if (source.contains(candidate)) {
return (E) candidate;
}
}
return null;
}
/**
* Find a single value of the given type in the given Collection.
* @param collection the Collection to search
* @param type the type to look for
* @return a value of the given type found if there is a clear match,
* or {@code null} if none or more than one such value found
*/
@SuppressWarnings("unchecked")
public static <T> T findValueOfType(Collection<?> collection, Class<T> type) {
if (isEmpty(collection)) {
return null;
}
T value = null;
for (Object element : collection) {
if (type == null || type.isInstance(element)) {
if (value != null) {
// More than one value found... no clear single value.
return null;
}
value = (T) element;
}
}
return value;
}
/**
* Find a single value of one of the given types in the given Collection:
* searching the Collection for a value of the first type, then
* searching for a value of the second type, etc.
* @param collection the collection to search
* @param types the types to look for, in prioritized order
* @return a value of one of the given types found if there is a clear match,
* or {@code null} if none or more than one such value found
*/
public static Object findValueOfType(Collection<?> collection, Class<?>[] types) {
if (isEmpty(collection) || ObjectUtils.isEmpty(types)) {
return null;
}
for (Class<?> type : types) {
Object value = findValueOfType(collection, type);
if (value != null) {
return value;
}
}
return null;
}
/**
* Determine whether the given Collection only contains a single unique object.
* @param collection the Collection to check
* @return {@code true} if the collection contains a single reference or
* multiple references to the same instance, {@code false} otherwise
*/
public static boolean hasUniqueObject(Collection<?> collection) {
if (isEmpty(collection)) {
return false;
}
boolean hasCandidate = false;
Object candidate = null;
for (Object elem : collection) {
if (!hasCandidate) {
hasCandidate = true;
candidate = elem;
}
else if (candidate != elem) {
return false;
}
}
return true;
}
/**
* Find the common element type of the given Collection, if any.
* @param collection the Collection to check
* @return the common element type, or {@code null} if no clear
* common type has been found (or the collection was empty)
*/
public static Class<?> findCommonElementType(Collection<?> collection) {
if (isEmpty(collection)) {
return null;
}
Class<?> candidate = null;
for (Object val : collection) {
if (val != null) {
if (candidate == null) {
candidate = val.getClass();
}
else if (candidate != val.getClass()) {
return null;
}
}
}
return candidate;
}
/**
* Retrieve the first element of the given Set, using {@link SortedSet#first()}
* or otherwise using the iterator.
* @param set the Set to check (may be {@code null} or empty)
* @return the first element, or {@code null} if none
* @since 5.2.3
* @see SortedSet
* @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet
*/
public static <T> T firstElement(Set<T> set) {
if (isEmpty(set)) {
return null;
}
if (set instanceof SortedSet) {
return ((SortedSet<T>) set).first();
}
Iterator<T> it = set.iterator();
T first = null;
if (it.hasNext()) {
first = it.next();
}
return first;
}
/**
* Retrieve the first element of the given List, accessing the zero index.
* @param list the List to check (may be {@code null} or empty)
* @return the first element, or {@code null} if none
* @since 5.2.3
*/
public static <T> T firstElement(List<T> list) {
if (isEmpty(list)) {
return null;
}
return list.get(0);
}
/**
* Retrieve the last element of the given Set, using {@link SortedSet#last()}
* or otherwise iterating over all elements (assuming a linked set).
* @param set the Set to check (may be {@code null} or empty)
* @return the last element, or {@code null} if none
* @since 5.0.3
* @see SortedSet
* @see LinkedHashMap#keySet()
* @see java.util.LinkedHashSet
*/
public static <T> T lastElement(Set<T> set) {
if (isEmpty(set)) {
return null;
}
if (set instanceof SortedSet) {
return ((SortedSet<T>) set).last();
}
// Full iteration necessary...
Iterator<T> it = set.iterator();
T last = null;
while (it.hasNext()) {
last = it.next();
}
return last;
}
/**
* Retrieve the last element of the given List, accessing the highest index.
* @param list the List to check (may be {@code null} or empty)
* @return the last element, or {@code null} if none
* @since 5.0.3
*/
public static <T> T lastElement(List<T> list) {
if (isEmpty(list)) {
return null;
}
return list.get(list.size() - 1);
}
/**
* Marshal the elements from the given enumeration into an array of the given type.
* Enumeration elements must be assignable to the type of the given array. The array
* returned will be a different instance than the array given.
*/
public static <A, E extends A> A[] toArray(Enumeration<E> enumeration, A[] array) {
ArrayList<A> elements = new ArrayList<>();
while (enumeration.hasMoreElements()) {
elements.add(enumeration.nextElement());
}
return elements.toArray(array);
}
/**
* Adapt an {@link Enumeration} to an {@link Iterator}.
* @param enumeration the original {@code Enumeration}
* @return the adapted {@code Iterator}
*/
public static <E> Iterator<E> toIterator(Enumeration<E> enumeration) {
return (enumeration != null ? new EnumerationIterator<>(enumeration) : Collections.emptyIterator());
}
/**
* Iterator wrapping an Enumeration.
*/
private static class EnumerationIterator<E> implements Iterator<E> {
private final Enumeration<E> enumeration;
public EnumerationIterator(Enumeration<E> enumeration) {
this.enumeration = enumeration;
}
@Override
public boolean hasNext() {
return this.enumeration.hasMoreElements();
}
@Override
public E next() {
return this.enumeration.nextElement();
}
@Override
public void remove() throws UnsupportedOperationException {
throw new UnsupportedOperationException("Not supported");
}
}
}
@@ -0,0 +1,909 @@
/*
* 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.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
/**
* 拷贝spring
*
* Miscellaneous object utility methods.
*
* <p>Mainly for internal use within the framework.
*
* <p>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.
* <p>This method supports the following object types.
* <ul>
* <li>{@code Optional}: considered empty if {@link Optional#empty()}</li>
* <li>{@code Array}: considered empty if its length is zero</li>
* <li>{@link CharSequence}: considered empty if its length is zero</li>
* <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
* <li>{@link Map}: delegates to {@link Map#isEmpty()}</li>
* </ul>
* <p>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 <em>empty</em>
* @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 <E> the concrete Enum type
* @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()}
* @param constant the constant to get the enum value of
* @throws IllegalArgumentException if the given constant is not found in the given array
* of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
*/
public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
for (E candidate : enumValues) {
if (candidate.toString().equalsIgnoreCase(constant)) {
return candidate;
}
}
throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " +
enumValues.getClass().getComponentType().getName());
}
/**
* Append the given object to the given array, returning a new array
* consisting of the input array contents plus the given object.
* @param array the array to append to (can be {@code null})
* @param obj the object to append
* @return the new array (of the same component type; never {@code null})
*/
public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {
Class<?> compType = Object.class;
if (array != null) {
compType = array.getClass().getComponentType();
}
else if (obj != null) {
compType = obj.getClass();
}
int newArrLength = (array != null ? array.length + 1 : 1);
@SuppressWarnings("unchecked")
A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
if (array != null) {
System.arraycopy(array, 0, newArr, 0, array.length);
}
newArr[newArr.length - 1] = obj;
return newArr;
}
/**
* Convert the given array (which may be a primitive array) to an
* object array (if necessary of primitive wrapper objects).
* <p>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}.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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.
* <p>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();
}
}
@@ -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<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
// 这里只进行基本检测, 用一些简单算法进行校验,如有需要可扩展
// 检测1: 滑动时间如果小于300毫秒 返回false
// 检测2: 轨迹数据要是少于背景宽度的五分之一,或者大于背景宽度的五分之一 返回false
// 检测3: x轴和y轴应该是从0开始的,要是一开始x轴和y轴乱跑,返回false
// 检测4: 如果y轴是相同的,必然是机器操作,直接返回false
// 检测5: x轴或者y轴直接的区间跳跃过大的话返回 false
// 检测6: x轴应该是由快到慢的, 要是速率一致,返回false
// 检测7: 如果x轴超过图片宽度的频率过高,返回false
// 检测1
if (startSlidingTime + 300 > endSlidingTime) {
return false;
}
// 检测2
if (trackList.size() < bgImageWidth / 5 || trackList.size() > bgImageWidth * 5) {
return false;
}
// 检测3
SliderCaptchaTrack.Track firstTrack = trackList.get(0);
if (firstTrack.getX() > 1 || firstTrack.getX() < -2 || firstTrack.getY() > 1 || firstTrack.getY() < -2) {
return false;
}
int check4 = 0;
int check7 = 0;
for (int i = 1; i < trackList.size(); i++) {
SliderCaptchaTrack.Track track = trackList.get(i);
int x = track.getX();
int y = track.getY();
// check4
if (firstTrack.getY() == y) {
check4++;
}
// check7
if (x >= bgImageWidth) {
check7++;
}
// check5
SliderCaptchaTrack.Track preTrack = trackList.get(i - 1);
if ((track.getX() - preTrack.getX()) > 5 || (track.getY() - preTrack.getY()) > 5) {
return false;
}
}
if (check4 > trackList.size() * 0.7 || check7 > 200) {
return false;
}
// check6
int splitPos = (int) (trackList.size() * 0.7);
SliderCaptchaTrack.Track splitPostTrack = trackList.get(splitPos - 1);
int posTime = splitPostTrack.getT();
float startAvgPosTime = posTime / (float) splitPos;
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
float endAvgPosTime = lastTrack.getT() / (float) (trackList.size() - splitPos);
return endAvgPosTime > startAvgPosTime;
}
public void checkParam(SliderCaptchaTrack sliderCaptchaTrack) {
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageWidth())) {
throw new IllegalArgumentException("bgImageWidth must not be null");
}
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getBgImageHeight())) {
throw new IllegalArgumentException("bgImageHeight must not be null");
}
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getSliderImageWidth())) {
throw new IllegalArgumentException("sliderImageWidth must not be null");
}
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getSliderImageHeight())) {
throw new IllegalArgumentException("sliderImageHeight must not be null");
}
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getStartSlidingTime())) {
throw new IllegalArgumentException("startSlidingTime must not be null");
}
if (ObjectUtils.isEmpty(sliderCaptchaTrack.getEntSlidingTime())) {
throw new IllegalArgumentException("entSlidingTime must not be null");
}
if (CollectionUtils.isEmpty(sliderCaptchaTrack.getTrackList())) {
throw new IllegalArgumentException("trackList must not be null");
}
for (SliderCaptchaTrack.Track track : sliderCaptchaTrack.getTrackList()) {
Integer x = track.getX();
Integer y = track.getY();
Integer t = track.getT();
if (x == null || y == null || t == null) {
throw new IllegalArgumentException("track[x,y,t] must not be null");
}
}
}
}
@@ -0,0 +1,65 @@
package cloud.tianai.captcha.template.slider.validator;
import cloud.tianai.captcha.template.slider.util.CollectionUtils;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
public class SimpleSliderCaptchaValidator implements SliderCaptchaValidator {
public static float DEFAULT_TOLERANT = 0.02f;
/** 容错值. */
@Getter
@Setter
public float defaultTolerant = DEFAULT_TOLERANT;
public SimpleSliderCaptchaValidator() {
}
public SimpleSliderCaptchaValidator(float defaultTolerant) {
this.defaultTolerant = defaultTolerant;
}
@Override
public float calcPercentage(int x, int bgImageWidth) {
return (float) x / bgImageWidth;
}
@Override
public boolean checkPercentage(Float newPercentage, Float oriPercentage) {
return checkPercentage(newPercentage, oriPercentage, defaultTolerant);
}
@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;
}
@Override
public boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage) {
Integer bgImageWidth = sliderCaptchaTrack.getBgImageWidth();
if (bgImageWidth == null || bgImageWidth < 1) {
// 没有背景图片宽度
return false;
}
List<SliderCaptchaTrack.Track> trackList = sliderCaptchaTrack.getTrackList();
if (CollectionUtils.isEmpty(trackList)) {
// 没有滑动轨迹
return false;
}
// 取最后一个滑动轨迹
SliderCaptchaTrack.Track lastTrack = trackList.get(trackList.size() - 1);
// 计算百分比
float calcPercentage = calcPercentage(lastTrack.getX(), bgImageWidth);
// 校验百分比
return checkPercentage(calcPercentage, oriPercentage);
}
}
@@ -0,0 +1,41 @@
package cloud.tianai.captcha.template.slider.validator;
import cloud.tianai.captcha.template.slider.util.CollectionUtils;
import cloud.tianai.captcha.template.slider.util.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @Author: 天爱有情
* @date 2022/2/17 9:23
* @Description 滑块验证码滑动轨迹
*/
@Data
public class SliderCaptchaTrack {
/** 背景图片宽度. */
private Integer bgImageWidth;
/** 背景图片高度. */
private Integer bgImageHeight;
/** 滑块图片宽度. */
private Integer sliderImageWidth;
/** 滑块图片高度. */
private Integer sliderImageHeight;
/** 滑动开始时间. */
private Date startSlidingTime;
/** 滑动结束时间. */
private Date entSlidingTime;
/** 滑动的轨迹. */
private List<Track> trackList;
@Data
@AllArgsConstructor
public static class Track {
private Integer x;
private Integer y;
private Integer t;
}
}
@@ -0,0 +1,46 @@
package cloud.tianai.captcha.template.slider.validator;
/**
* @Author: 天爱有情
* @date 2022/2/17 10:54
* @Description 滑块验证码校验器
*/
public interface SliderCaptchaValidator {
/**
* 计算滑块要背景图的百分比,基本校验
*
* @param x 凹槽的x轴
* @param bgImageWidth 背景图片的宽度
* @return float
*/
float calcPercentage(int x, int bgImageWidth);
/**
* 校验滑块百分比
*
* @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 sliderCaptchaTrack 包含了滑动轨迹,展示的图片宽高,滑动时间等参数
* @param oriPercentage 正确的滑块百分比,用作基础校验
* @return boolean
*/
boolean valid(SliderCaptchaTrack sliderCaptchaTrack, Float oriPercentage);
}