项目实施-12 (数据抽取 贰)

概述

本篇承接项目实施-11 https://blog.csdn.net/ASYMUXUE/article/details/105353601继续介绍 风险评估数据 的抽取。

抽取验证数据

思路总结: 我们通过 从登录的日志中 获取到用户登录的数据:

  • 对此数据进行实体类封装
  • 建立评估因子模型,确定要对哪些数据进行评估
  • 创建评估报告实体类,作为传递最终判断结果数据的封装模型
  • 采用责任链设计模式,对评估因子一一进行评估判断
  • 判定每个评估因子的评估结果,从此次需要验证的数据中获取与之历史数据进行对比
  • 将每个因子的判定结果放入 评估报告 ,进行报告结果数据的向下传递
创建验证数据的实体类
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

发布了42 篇原创文章 · 获赞 6 · 访问量 1743

猜你喜欢

转载自blog.csdn.net/ASYMUXUE/article/details/105421616
今日推荐