概述
用户在填写登陆表单时,每次输入用户名密码或者验证码的所花费的时间是存在一定的规律的。多次正常输入在二维坐标系(用户名,密码),会呈现一个圆形,三维坐标系(用户名,密码,验证码)则会呈现一个球形,多维则亦然。
评估当前用户输入如果不在正常输入范围内,则说明此次登陆存在一定的风险。
计算步骤:
- 1.计算圆⼼中点
- 2.计算两两特征距离
- 3.对距离进⾏排序(升序),取2/3处作为评估距离阈值 - threshold
- 4.计算当前输⼊的特征距离中⼼点距离d
数学公式
欧氏距离
n点两两组合
- n(n-1)/2
数据支持
评估数据
- 1.用户当前登录的输入特性(每个控件输入时间ms)
- 2.用户历史输入特性(最多留存最近10条数据)
java代码示例:
/**
* 用户登陆输入特性
*/
public class FeatureEvaluate {
/**
* 1.计算圆⼼中点
* 2.计算两两特征距离
* 3.对距离进⾏排序(升序),取2/3处作为评估距离阈值 - threshold
* 4.计算当前输⼊的特征距离中⼼点距离d
* @param loginFeature 登陆特性
* @param historyFeature 历史输入特性
* @return 有风险,true
*/
public Boolean doEval(Double[] loginFeature, List<Double[]> historyFeature){
//保证历史数据至少有两条
if(historyFeature == null || historyFeature.size() < 2)
return false;
//1.计算圆心,所有坐标均值
List<Double> centerVector = Arrays.stream(historyFeature.stream()
.reduce((v1, v2) -> {
Double[] sum = new Double[v1.length];//历史数据每个维度的总和数组
for (int i = 0; i < v1.length; i++) {
if (sum[i] == null)
sum[i] = 0.0;
sum[i] += v1[i] + v2[i];
}
return sum;
}).get())//所有坐标值的和
.map(s -> (s * 1.0) / historyFeature.size())
.collect(Collectors.toList());
//圆心字符串形式
String strCenterVector = String.join(",", centerVector.stream()
.map(s -> s + "")
.collect(Collectors.toList()));
System.out.println("圆心:"+strCenterVector);//展示圆心
//2.计算两两特性距离
List<Double> distanceList = new ArrayList<>();
for (int i = 0; i < historyFeature.size(); i++) {
for (int j = i + 1; j < historyFeature.size(); j++) {
distanceList.add(distance(historyFeature.get(i),historyFeature.get(j)));
}
}
System.out.println("两两距离:"+distanceList);//展示距离
//3.对距离进⾏排序(升序),取2/3处作为评估距离阈值 - threshold
List<Double> sortedDistanceList = distanceList.stream()
.sorted()
.collect(Collectors.toList());
Double thresholdDistance = sortedDistanceList.get(sortedDistanceList.size() * 2 / 3);
System.out.println("半径:" + thresholdDistance);
//4.计算当前输入特性与圆心的距离
Double distance = distance(loginFeature, centerVector.toArray(new Double[0]));
System.out.println("当前输入与圆心的距离:" + distance);
return distance > thresholdDistance;
}
/**
* 计算两点距离
* @param v1
* @param v2
* @return
*/
public Double distance(Double[] v1,Double[] v2){
Double sum = 0.0;
for (int i = 0; i < v1.length; i++) {
sum += (v1[i] - v2[i]) * (v1[i] - v2[i]);
}
return Math.sqrt(sum);
}
//测试
public static void main(String[] args) {
FeatureEvaluate inputFeatureEvaluate = new FeatureEvaluate();
ArrayList<Double[]> latestInputFeatures = new ArrayList<>();
latestInputFeatures.add(new Double[]{1000.0,1100.0,1800.0});
latestInputFeatures.add(new Double[]{1100.0,1120.0,1750.0});
latestInputFeatures.add(new Double[]{950.0,1250.0,2000.0});
latestInputFeatures.add(new Double[]{1200.0,1050.0,1900.0});
latestInputFeatures.add(new Double[]{1400.0,800.0,2500.0});
inputFeatureEvaluate.doEval(new Double[]{1100.0,1000.0,1750.0},latestInputFeatures);
}
}