概述
本篇承接项目实施-11
https://blog.csdn.net/ASYMUXUE/article/details/105353601继续介绍 风险评估数据
的抽取。
- 本篇涉及部分简单算法,请参考https://blog.csdn.net/ASYMUXUE/category_9862606.html
抽取验证数据
思路总结: 我们通过
从登录的日志中
获取到用户登录的数据:
- ①
对此数据进行实体类封装
- ②
建立评估因子模型,确定要对哪些数据进行评估
- ③
创建评估报告实体类,作为传递最终判断结果数据的封装模型
- ④
采用责任链设计模式,对评估因子一一进行评估判断
- ⑤
判定每个评估因子的评估结果,从此次需要验证的数据中获取与之历史数据进行对比
- ⑥
将每个因子的判定结果放入
评估报告 ,进行报告结果数据的向下传递
①创建验证数据的实体类
package com.baizhi.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true)
public class EvaluateData implements Serializable {
private long evaluateTime;
//应用名
private String applicationName;
//用户的唯一标识
private String userIdentify;
//登录序列号
private String loginSequence;
private String ordernessPassword;
private String cityName;
//地理位置经纬度,其对象的创建在 第 11 篇中。
private GeoPoint geoPoint;
//输入特征 (输入时长)
private Double[] inputFeatures;
//设备信息
private String deviceInformation;
}
/*
* //INFO 2020-03-31 10:12:00 QQ EVALUATE [张三] 6ebaf4ac780f40f486359f3ea6934620 "12355421" Beijing "116.4,39.5"
//[1200,15000,2100] "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
* */
②创建评估因子
package com.baizhi.entities;
/**
* 声明所有的风险元素
*/
public enum RiskFactor {
AREA("area"), //地址
DEVICE("device"), //设备
TOTAL("total"), //次数
TIMESLOT("timeslot"), //访问时段
SIMILARITY("similarity"),//密码相似度
INPUTFEATURE("inputfeature"),//输入特征
SPEED("speed");//位移速度
private String name;
//提供构造
RiskFactor(String name){
this.name=name;
}
}
③创建评估报告
package com.baizhi.entities;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
//评估报告
public class EvaluateReport implements Serializable {
private String applicationName;
private String userIdentify;
private String loginSequence;
private long evaluateTime;
private String cityName;
private GeoPoint geoPoint;
//map集合,存储所有的风险因子属性
private Map<RiskFactor,Boolean> metrics=new HashMap<RiskFactor,Boolean>();
//定下特定风险因子的报告(改变属性的特定值)
public void signReport(RiskFactor riskFactor,boolean flag){
metrics.put(riskFactor,flag);
}
public EvaluateReport(String applicationName, String userIdentify, String loginSequence, long evaluateTime, String cityName, GeoPoint geoPoint) {
this.applicationName = applicationName;
this.userIdentify = userIdentify;
this.loginSequence = loginSequence;
this.evaluateTime = evaluateTime;
this.cityName = cityName;
this.geoPoint = geoPoint;
//初始化所有风险因子都是false
metrics.put(RiskFactor.AREA,false);
metrics.put(RiskFactor.DEVICE,false);
metrics.put(RiskFactor.SIMILARITY,false);
metrics.put(RiskFactor.SPEED,false);
metrics.put(RiskFactor.TIMESLOT,false);
metrics.put(RiskFactor.INPUTFEATURE,false);
metrics.put(RiskFactor.TOTAL,false);
}
/**
* AREA DEVICE INPUTFEATURE SIMILARITY SPEED TIMESLOT TOTAL
* QQ zhangsan 001 1585640451510 Beijing 116.4,39.5 true false false false true false false
* @return
*/
@Override
public String toString(){
String report=metrics.keySet()
.stream()
.sorted((RiskFactor o1, RiskFactor o2) -> o1.name().compareTo(o2.name()))
.map(riskFactor -> metrics.get(riskFactor)+"")
.reduce((v1,v2)->v1+" "+v2)
.get();
return applicationName+" "+userIdentify+" "+loginSequence+" "+evaluateTime+" "+cityName+ " "+geoPoint.getLongtitude()+","+geoPoint.getLatitude()+" "+report;
}
public static void main(String[] args) {
System.out.println( new EvaluateReport("QQ","zhangsan","001",new Date().getTime(),"Beijing",new GeoPoint(116.4,39.5)));
}
public String getApplicationName() {
return applicationName;
}
public String getUserIdentify() {
return userIdentify;
}
}
④创建评估链
package com.baizhi.evaluate;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import java.util.List;
public class EvaluateChain {
//验证位置
private int position=0;
//评估链(责任链)
private List<Evaluate> evaluates;
//构造
public EvaluateChain(List<Evaluate> evaluates) {
this.evaluates = evaluates;
}
//向下执行
public void doChain(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport){
if(position < evaluates.size()){
//获取一个责任
Evaluate evaluate = evaluates.get(position);
position +=1;
evaluate.eval(evaluateData,historyData,evaluateReport,this);
}
}
}
⑤创建验证的抽象类
(后续每个因子的评估类都将继承此抽象类)
package com.baizhi.evaluate;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
//验证抽象类
public abstract class Evaluate {
//风险因子
private RiskFactor riskFactor;
//构造
public Evaluate(RiskFactor riskFactor) {
this.riskFactor = riskFactor;
}
//获取验证的当前风险
public RiskFactor getRiskFactor() {
return riskFactor;
}
/**
*验证方法
* @param evaluateData
* @param historyData
* @param evaluateReport
* @param evaluateChain:驱动下一个Evaluate实例
*/
public abstract void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport,
EvaluateChain evaluateChain);
}
⑥继承抽象类
登录城市验证
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.Set;
public class AreaEvaluate extends Evaluate {
public AreaEvaluate() {
super(RiskFactor.AREA);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getCityName(),historyData.getHistoryCities()));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
public boolean doEval(String cityName, Set<String> historyCities){
if(historyCities==null || historyCities.size()==0){//说明是第一次使用
return false;
}else{//如果登录过该城市就没有风险
//不包含,返回True
return !historyCities.contains(cityName);
}
}
}
登录设备验证
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.List;
import java.util.Set;
public class DeviceEvaluate extends Evaluate {
public DeviceEvaluate() {
super(RiskFactor.DEVICE);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getDeviceInformation(),historyData.getHistoryDeviceInformations()));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
public boolean doEval(String deviceInformation, List<String> historyDeviceInformations){
if(historyDeviceInformations==null || historyDeviceInformations.size()==0){//说明是第一次使用
return false;
}else{//如果没有使用该设备,返回true 有风险
return !historyDeviceInformations.contains(deviceInformation);
}
}
}
登录密码验证
余弦相似度☆
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import javax.sql.rowset.serial.SQLOutputImpl;
import java.util.*;
import java.util.stream.Collectors;
public class SimilarityEvaluate extends Evaluate {
private Double thresholdSimilarity;
public SimilarityEvaluate(Double thresholdSimilarity) {
super(RiskFactor.SIMILARITY);
this.thresholdSimilarity=thresholdSimilarity;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getOrdernessPassword(),historyData.getHistoryOrdernessPasswords()));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
//作评估
public boolean doEval(String ordernessPassword, Set<String> historyOrdernessPasswords){
//如果历史密码没有,则证明第一次登陆,不校验
if( historyOrdernessPasswords==null || historyOrdernessPasswords.size()==0){
return false;
}
//获取包含所有历史密码的词袋
Set<Character> wordBag = new HashSet<Character>();
//获取历史密码流
List<char[]> collect = historyOrdernessPasswords.stream().map(histroyt -> histroyt.toCharArray()).collect(Collectors.toList());
for (char[] chars : collect) {
for (int i = 0; i < chars.length; i++) {
//词袋获取所有历史的单词
wordBag.add(chars[i]);
}
}
//将当前输入的密码加入词袋
char[] chars = ordernessPassword.toCharArray();
for (int i = 0; i < chars.length; i++) {
wordBag.add(chars[i]);
}
//对词袋进行排序,排序后的最终词袋
List<Character> bag = wordBag.stream().sorted().collect(Collectors.toList());
//创建一个历史密码的特征向量集合
List<Integer[]> histroryVictorList = new ArrayList<>();
//计算历史密码的特征
List<String> history = historyOrdernessPasswords.stream().collect(Collectors.toList());
for (int i = 0; i < history.size(); i++) {
Integer[] passwordVector = getPasswordVector(bag, history.get(i));
//向集合中存下一个特性
histroryVictorList.add(passwordVector);
}
//计算当前输入密码的特征向量
Integer[] tempVictor = getPasswordVector(bag, ordernessPassword);
//拿当前的密码特征向量与历史的特征向量进行余弦相似比较
/*for (int i = 0; i < histroryVictorList.size(); i++) {
Double result = getSimilarity(tempVictor, histroryVictorList.get(i));
//如果相似度小于阈值,则判定有风险
if(result<thresholdSimilarity){
return true;
}
}*/
List<Double> result = histroryVictorList.stream().map(x -> {
Double similarity = getSimilarity(tempVictor, x);
System.out.println("当前密码相似度:" + similarity);
return similarity;
}).filter(similarity -> similarity > thresholdSimilarity).collect(Collectors.toList());
return result.size()==0;
}
//创建一个计算特征向量的方法
private Integer[] getPasswordVector(List<Character> bag,String ordernessPassword){
//创建一个Map存储当前密码的特性
HashMap<Character, Integer> map = new HashMap<>();
char[] chars = ordernessPassword.toCharArray();
for (int i = 0; i < chars.length; i++) {
//如过集合中已经存在这个的key,则加一
if(map.containsKey(chars[i])){
map.put(chars[i],map.get(chars[i])+1);
}else{
//如果不存在,则存1
map.put(chars[i],1);
}
}
//创建一返回值,长度即为词袋长度
Integer[] restult = new Integer[bag.size()];
//对词袋数值进行升序排列
//对比词袋,进行特性抽取
for (int i = 0; i < bag.size(); i++) {
//如果这个位置的key,存在于map中,则,返回map的数值,否则,返回0
if(map.containsKey(bag.get(i))){
restult[i]=map.get(bag.get(i));
}else{
restult[i]=0;
}
}
//最后返回Integer数组
return restult;
}
//创建一个求余弦相似的方法
private Double getSimilarity(Integer[] passwordVictor,Integer[] historyVictor){
/**
* 根据余弦求相似公式得
计算分子部分 (首先可以确定的是,两个密码的特征向量的长度一致)
*/
//给出一个接收分子数值的参数
Double element = 0.0;
for (int i = 0; i < passwordVictor.length; i++) {
element += passwordVictor[i]*historyVictor[i];
}
//计算分母
Integer integer1 = Arrays.stream(passwordVictor).map(i -> (i * i)).reduce((x, y) -> x + y).get();
Integer integer2 = Arrays.stream(historyVictor).map(i -> (i * i)).reduce((x, y) -> x + y).get();
//按照公式得出最终的分母部分
Double denominator = Math.sqrt(integer1)*Math.sqrt(integer2);
//得出最终结果
return element/denominator;
}
public static void main(String[] args) {
int i = new Random().nextInt(4);
System.out.println(i);
}
}
登录次数验证(当天)
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.Set;
public class TotalEvaluate extends Evaluate {
private Integer threshold=0;
/**
* 允许用户最大的登录次数
* @param threshold
*/
public TotalEvaluate(Integer threshold) {
super(RiskFactor.TOTAL);
this.threshold=threshold;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
//改变报告属性
evaluateReport.signReport(getRiskFactor(),doEval(historyData.getCurrentDayLoginCount()));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
//具体评估
public boolean doEval(Integer currentDayLoginCount){
if(currentDayLoginCount==null){//说明是第一次使用
return false;
}else{//如果登录超过限定次数
return currentDayLoginCount >= threshold;
}
}
}
登录时间段验证(判断是否是常用时间)
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import sun.misc.Cleaner;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TimeSlotEvaluate extends Evaluate {
private int threshold;
public TimeSlotEvaluate(int threshold) {
super(RiskFactor.TIMESLOT);
this.threshold=threshold;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getEvaluateTime(),historyData.getHistoryLoginTimeSlot(),this.threshold));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
* @param evaluateTime
* @param historyLoginTimeSlot
* @param threshold :设定多累计登录多少次以后再对用户进行评估
* @return
*/
public boolean doEval(long evaluateTime, Map<String, Map<String,Integer>> historyLoginTimeSlot,int threshold){
String[] WEEKS={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(evaluateTime);
String dayOfWeek = WEEKS[calendar.get(Calendar.DAY_OF_WEEK) - 1];
DecimalFormat decimalFormat=new DecimalFormat("00");
String hourOfDay= decimalFormat.format(calendar.get(Calendar.HOUR_OF_DAY));//01 02 ... 24
//用户第一次登录
if(historyLoginTimeSlot==null || historyLoginTimeSlot.size()==0){
return false;
}
//用户是否达到评估计算阈值标准,如果登录总次小于阈值,不做评估
if(!historyLoginTimeSlot.containsKey(dayOfWeek)){
Integer totalCount = historyLoginTimeSlot.entrySet()
.stream()// 星期几 Map<小时,次数>
.map(t -> t.getValue().entrySet().stream().map(v -> v.getValue()).reduce((v1, v2) -> v1 + v2).get()) // 每天登录总数
.reduce((v1, v2) -> v1 + v2)
.get();
return totalCount >= threshold;
}else{
//获取当天的登录时段数据
Map<String, Integer> historyTimeSlot = historyLoginTimeSlot.get(dayOfWeek);
if(!historyTimeSlot.containsKey(hourOfDay)){//该天登录过,但是在该时段没有登录
return true;
}else{//该天登录过,但是在该时段也登录过
//判断当前的登录时段是否使用户的登录习惯
Integer currentHourLoginCount = historyTimeSlot.get(hourOfDay);
//升序登录时间段集合
List<Integer> sortedList = historyTimeSlot.entrySet()
.stream()
.map(t -> t.getValue()) //每个时段登录总和
.sorted().collect(Collectors.toList());
//获取用户登录时段的阈值,大于或者等于该值都是习惯
Integer thresholdTimeSlotCount=sortedList.get((sortedList.size()*2)/3);
return currentHourLoginCount<thresholdTimeSlotCount;
/*
//计算出所有登录总次数大于thresholdTimeSlotCount值所有hourOfDay集合-习惯时段
List<String> habbitTimeSlot = historyTimeSlot.entrySet()
.stream()
.filter(t -> t.getValue() >= thresholdTimeSlotCount)
.map(t -> t.getKey())
.collect(Collectors.toList());
return !habbitTimeSlot.contains(hourOfDay);
*/
}
}
}
}
输入特征验证
欧式距离☆
package com.baizhi.evaluate.impl;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.RiskFactor;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class InputFeatureEvaluate extends Evaluate {
public InputFeatureEvaluate() {
super(RiskFactor.INPUTFEATURE);
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
//填写报告
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getInputFeatures(),historyData.getLatestInputFeatures()));
//路由请求s
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
/**
* 1.计算圆心中点
* 2.计算两两特征距离
* 3.对距离进行排序(升序),取2/3处作为评估距离阈值 - threshold
* 4.计算当前输入的特征距离中心点距离d
* @param inputFeatures
* @param historyInputFeatures
* @return
*/
public boolean doEval(Double[] inputFeatures, List<Double[]> historyInputFeatures){
//如果特征不足2次,不进行验证
if(historyInputFeatures==null || historyInputFeatures.size()<2){
return false;
}
//已经足够2次
/**
* 第一步,计算圆心
*/
//计算出圆心,所有点的同一轴下的坐标长度相加/点的数量
Double[] centerOfCircle = historyInputFeatures.stream().reduce((x, y) -> {
//创建一个double 数组,用于接收圆心数据,数组长度为历史数据的维度
Double[] centerOfCircles = new Double[historyInputFeatures.get(0).length];
//获取每个轴下对应数据的总和
for (int i = 0; i < historyInputFeatures.get(0).length; i++) {
if (centerOfCircles[i] == null) {
centerOfCircles[i] = 0.0;
}
centerOfCircles[i] += x[i] + y[i];
}
return centerOfCircles;
}).get();
//计算最终的圆心结果
for (int i = 0; i < centerOfCircle.length; i++) {
centerOfCircle[i] = centerOfCircle[i]/historyInputFeatures.size();
}
System.out.println("计算的圆心位置是\t"+ Arrays.stream(centerOfCircle).map(x->x+"").reduce((v1,v2)->v1+","+v2).get());
/**
* 第二步,进行半径的计算,求出所有2点之间的半径,排序后取2/3的位置作为半径阈值
*/
//创建一个接收所有距离的数组,长度是 (设历史点个数为n)(n*n-1)/2
//确定数组长度
int size = ((historyInputFeatures.size())*(historyInputFeatures.size()-1))/2;
List<Double> allDistance = new ArrayList<Double>();
for (int i = 0; i < historyInputFeatures.size(); i++) {
//每次,拿1个历史数据点,和其他的数据进行距离计算
for (int j = i+1; j < historyInputFeatures.size(); j++) {
//计算每2个点的距离,并存入集合中
allDistance.add(getDistance(historyInputFeatures.get(i),historyInputFeatures.get(j)));
}
}
//排序,求出2/3位置的长度,作为半径
allDistance.sort((a,b)->{
if(a==b){
return 0;
}else{
return a>b?1:-1;
}
});
//得出半径
Double radius = allDistance.get((allDistance.size() * 2) / 3);
System.out.println("计算的半径是\t"+radius);
/**
* 第三步,判断当前特征的模型点到圆心的距离是否小于半径,是就无风险,否则有风险
*/
//获取当前的点的距离圆心的长度
Double result = getDistance(inputFeatures,centerOfCircle);
System.out.println("当前的点距离圆心的长度为"+result);
return result > radius;
}
//提供一个计算2点之间距离的方法 ∑(Xi-Yi)^2
private Double getDistance(Double[] point1,Double[] point2){
//创建一个变量,用于接收,每次求和之后的值
Double sum = 0.0;
for (int i = 0; i < point1.length; i++) {
sum += Math.pow(point1[i]-point2[i],2);
}
//最后返回计算结果
return Math.sqrt(sum);
}
public static void main(String[] args) {
InputFeatureEvaluate inputFeatureEvaluate = new InputFeatureEvaluate();
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);
}
}
登录位移(平均时速)验证
球面距离☆
package com.baizhi.evaluate.impl;
import com.baizhi.entities.*;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Set;
import static java.lang.Math.*;
public class SpeedEvaluate extends Evaluate {
private Double thresholdSpeed;
private static final Double EARTH_RADIUS=6371.393;//千米
public SpeedEvaluate(Double thresholdSpeed) {
super(RiskFactor.SPEED);
this.thresholdSpeed=thresholdSpeed;
}
@Override
public void eval(EvaluateData evaluateData, HistoryData historyData, EvaluateReport evaluateReport, EvaluateChain evaluateChain) {
evaluateReport.signReport(getRiskFactor(),doEval(evaluateData.getEvaluateTime(),evaluateData.getGeoPoint(),historyData.getLastLoginTime(),historyData.getLastLoginGeoPoint()));
//驱动调用下一个评估
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
}
public boolean doEval(long evaluateTime, GeoPoint currentGeoPoint,long lastLoginTime,GeoPoint lastLoginGeoPoint){
//判断如果没有上一次登陆的地理位置,则不进行评估
if(lastLoginGeoPoint==null){
return false;
}else {
//如果2点的经纬度,相同,则不进行验证
if(lastLoginGeoPoint.getLatitude()==currentGeoPoint.getLatitude() && lastLoginGeoPoint.getLongtitude()==currentGeoPoint.getLongtitude()){
System.out.println("没有发生位移,不进行位移验证");
return false;
}
/**
* 第一步,求2个时间段的时间差
*/
//当前时间减去上一次的登录时间 单位时间 小时 3600*1000 ==>得到1小时的毫秒值
Double time = (evaluateTime-lastLoginTime)*1.0/(3600*1000);
/**
* 第二步,计算地球上2个坐标之间的距离
*/
Double distanc = geoDistance(currentGeoPoint,lastLoginGeoPoint);
System.out.println("此次位移验证两地相距"+distanc+"\t两次时间差是"+time+"小时");
/**
* 第三步,计算位移的平均速度
*/
Double speed = distanc/time ;
System.out.println("speed = " + speed);
return speed>thresholdSpeed;
}
}
//提供一个计算球体上两点间距离的方法 S = R*arCos(COSw1*COSw2*COS(j2-ji)+SINw1*SINw2)
public Double geoDistance(GeoPoint currentGeo,GeoPoint lastGeo){
//将角速值转换为弧度
//w - 表示 纬度 j - 表示经度
Double Aw = toRadians(currentGeo.getLatitude());
Double Aj = toRadians(currentGeo.getLongtitude());
Double Bw = toRadians(lastGeo.getLatitude());
Double Bj = toRadians(lastGeo.getLongtitude());
//使用公式进行计算
Double acos = acos(cos(Aw) * cos(Bw) * cos(Bj - Aj) + sin(Aw) * sin(Bw));
return EARTH_RADIUS*acos;
}
public static void main(String[] args) throws ParseException {
SpeedEvaluate speedEvaluate = new SpeedEvaluate(600.0);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long evaluateTime=sdf.parse("2020-04-01 10:10:00").getTime();
GeoPoint p1=new GeoPoint();
p1.setLongtitude(116.2317);//北京
p1.setLatitude(39.5427);
long lastLoginTime=sdf.parse("2020-04-01 08:00:00").getTime();
GeoPoint p2=new GeoPoint();
p2.setLongtitude(114.14);//郑州
p2.setLatitude(34.16);
speedEvaluate.doEval(evaluateTime,p1,lastLoginTime,p2);
}
}
至此,我们此次项目的核心计算开发已完成,后续篇章将讲述,与大数据的
流计算引擎 的集成使用,以及
数据仓库ETL 的开发示例。
测试
在正式开启流计算之前,我们先要确保此计算模块的正确性
package com.baizh;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.EvaluateReport;
import com.baizhi.entities.HistoryData;
import com.baizhi.entities.LoginSuccessData;
import com.baizhi.evaluate.Evaluate;
import com.baizhi.evaluate.EvaluateChain;
import com.baizhi.evaluate.impl.*;
import com.baizhi.update.Updater;
import com.baizhi.update.UpdaterChain;
import com.baizhi.update.impl.*;
import com.baizhi.util.EvaluateUtil;
import org.junit.Before;
import org.junit.Test;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
public class UpdateChainEvaluateTests {
String[] testData={
"INFO 2020-03-31 10:12:00 QQ SUCCESS [张三] 6ebaf4ac780f40f486359f3ea6934620 \"123456\" Beijing \"116.4,39.5\" [1250,14000,2000] \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\"",
"INFO 2020-04-02 10:12:00 QQ SUCCESS [张三] 6ebaf4ac780f40f486359f3ea6934621 \"123456\" Beijing \"116.4,39.5\" [1200,15800,2100] \"Mozilla/6.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\"",
"INFO 2020-04-02 10:50:00 QQ SUCCESS [张三] 6ebaf4ac780f40f486359f3ea6934622 \"123456\" Beijing \"116.4,39.5\" [1300,17000,2200] \"Mozilla/7.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\"",
"INFO 2020-04-03 10:12:00 QQ SUCCESS [张三] 6ebaf4ac780f40f486359f3ea6934623 \"456123\" Zhengzhou \"114.4,34.5\" [1400,16000,2100] \"Mozilla/8.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\""
};
String evaluateData= "INFO 2020-04-03 10:12:01 QQ EVALUATE [张三] 6ebaf4ac780f40f486359f3ea6934620 \"123458\" Beijing \"116.4,39.5\" [1250,14000,2000] \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36\"";
private List<Updater> updaters;
private List<Evaluate> evaluates;
@Before
public void before(){
updaters=new ArrayList<Updater>();
updaters.add(new CitiesUpdates());
updaters.add(new DeviceUpdates(3));
updaters.add(new InputFeaturesUpdater());
updaters.add(new LastLoginGeoPoint());
updaters.add(new LoginTimeUpdater());
updaters.add(new PasswordsUpdates());
updaters.add(new TimeSlotUpdater());
evaluates=new ArrayList<Evaluate>();
evaluates.add(new AreaEvaluate());
evaluates.add(new DeviceEvaluate());
evaluates.add(new InputFeatureEvaluate());
evaluates.add(new SimilarityEvaluate(0.9));
evaluates.add(new SpeedEvaluate(750.0));
evaluates.add(new TimeSlotEvaluate(1));
evaluates.add(new TotalEvaluate(2));
}
@Test
public void testUpdate() throws ParseException {
HistoryData historyData = new HistoryData();
for (int i = 0; i < testData.length; i++) {
String data = testData[i];
UpdaterChain updaterChain = new UpdaterChain(updaters);
LoginSuccessData loginSuccessData= EvaluateUtil.parseLoginSuccessData(data);
updaterChain.doChain(loginSuccessData,historyData);
}
System.out.println(historyData);
}
@Test
public void testEvaluate() throws ParseException {
HistoryData historyData = new HistoryData();
for (int i = 0; i < testData.length; i++) {
String data = testData[i];
UpdaterChain updaterChain = new UpdaterChain(updaters);
LoginSuccessData loginSuccessData= EvaluateUtil.parseLoginSuccessData(data);
updaterChain.doChain(loginSuccessData,historyData);
}
historyData.setCurrentDayLoginCount(100);
EvaluateData evaluateData = EvaluateUtil.parseEvaluateData(this.evaluateData);
EvaluateReport evaluateReport = new EvaluateReport(
evaluateData.getApplicationName(),
evaluateData.getUserIdentify(),
evaluateData.getLoginSequence(),
evaluateData.getEvaluateTime(),
evaluateData.getCityName(),
evaluateData.getGeoPoint());
EvaluateChain evaluateChain = new EvaluateChain(evaluates);
evaluateChain.doChain(evaluateData,historyData,evaluateReport);
System.out.println(" AREA DEVICE INPUTFEATURE SIMILARITY SPEED TIMESLOT TOTAL");
System.out.println(evaluateReport);
}
}
/*false true false true true false true*/
附
附上此次项目的 数据验证模块的完整原代码
:
https://pan.baidu.com/s/1l9H0cSNSP3-JbYcyS82vqA 获取码:3zpu