定义一个接口,声明一个方法,返回一个tuple,表示某一项是否存在风险
trait RiskEvaluate {
/**
* @param currentValue 当前评估值
* @param historyValue 历史评估值
* @return
*/
def evaluate(currentValue:Any,historyValue:Any):(String,Boolean)
}
写一个类,继承RiskEvaluate接口,覆盖接口方法,表示输入习惯(输入向量)是否存在异常
package com.evaluate.impl
import com.baizhi.evaluate.RiskEvaluate
import scala.collection.mutable.ListBuffer
class InputVectorEvaluate extends RiskEvaluate{
/**
*
* @param currentInputVector:当前输入向量
* @param historyVector:历史输入向量数组
* @return
*/
def doEvaluate(currentInputVector: Array[Double], historyVector: ListBuffer[Array[Double]]): (String, Boolean) = {
var n=historyVector.length
if(n<2){
return ("FORM_INPUT",false)
}
//计算平均向量
var avgVector=new Array[Double](currentInputVector.length)
for(i <- 0 until currentInputVector.length ){
avgVector(i) = historyVector.map(v=>v(i)).reduce(_+_)/historyVector.length
}
//n个历史向量间 彼此距离
var distances=new ListBuffer[Double]
for(i<- historyVector ;j <- historyVector;if(i!=j)){ //任意两个点坐标的距离
//必须保证 i!=j 计算 i和j的距离
var total:Double=0
for( item<- 0 until avgVector.length){
total+= Math.pow((i(item)-j(item)),2)
}
//添加距离
distances += Math.sqrt(total)
//将向量从historyVector中移除
historyVector -= i
}
//对distances结果进行排序
distances=distances.sortBy(distance=>distance)
//计算当前输入向量和平均向量的距离
var curentDistance:Double=0
for( item<- 0 until avgVector.length){
curentDistance+= Math.pow((avgVector(item)-currentInputVector(item)),2)
}
curentDistance=Math.sqrt(curentDistance)
//获取比对阈值
var threshold: Double = distances((n * (n - 1))/6)
println("距离:"+distances.map(t=>t+"").reduce(_+","+_))
println("avgVector:"+avgVector.map(t=>t+"").reduce(_+","+_))
println("currentInputVector:"+currentInputVector.map(t=>t+"").reduce(_+","+_))
println("threshold: "+threshold+" curentDistance: "+curentDistance)
return ("FORM_INPUT",curentDistance>threshold)
}
override def evaluate(v1: Any, v2: Any): (String, Boolean) = {
doEvaluate(v1.asInstanceOf[Array[Double]],v2.asInstanceOf[ListBuffer[Array[Double]]])
}
}
写一个类,继承RiskEvaluate接口,覆盖接口方法,表示密码重试是否存在异常(根据历史密码词袋判断)
通过计算用户当前密码和历史密码做余弦相似度计算,计算出当前向量之间的夹角,通过向量夹角评估用户的输入是否存在异常。
package com.evaluate.impl
import java.util
import com.baizhi.evaluate.RiskEvaluate
import scala.collection.mutable.ListBuffer
class PasswordEvaluate extends RiskEvaluate{
/**
*
* @param currentPawssword:当前输入向量
* @param historyPasswords:历史输入向量数组
* @return
*/
def doEvaluate(currentPawssword: String, historyPasswords: ListBuffer[String]): (String, Boolean) = {
val wordBag = historyPasswords.flatMap(password=> password.toCharArray).distinct.toList.sortBy(c=>c)
println("词袋:"+wordBag.mkString(","))
var historyPwddwordVectors=new ListBuffer[Array[Int]]
for(pwd<-historyPasswords){
val vector = convertString2Vectors(wordBag,pwd)
historyPwddwordVectors += vector
}
val currentVector = convertString2Vectors(wordBag,currentPawssword)
var simlarity:Double=0.0
for(v <- historyPwddwordVectors){
var s:Double=calculateSimlarity(currentVector,v)
println("s:"+s)
if(s>simlarity){ //获取自大相似度
simlarity=s
}
}
if(simlarity < 0.95){
return ("PASSWORD_INPUT",true)
}
return ("FORM_INPUT",false)
}
private def calculateSimlarity(c: Array[Int], v: Array[Int]): Double = {
var vectorMutli=0.0
for(i <- 0 until c.length){
vectorMutli +=c(i)*v(i)
}
return vectorMutli/(Math.sqrt(c.map(item=>item*item).reduce(_+_))* Math.sqrt(v.map(item=>item*item).reduce(_+_)))
}
private def convertString2Vectors(wordBag:List[Char],pwd:String):Array[Int]={
val charMap = new util.HashMap[Char,Int]()
for(c<-pwd.toCharArray){
if(charMap.containsKey(c)){
charMap.put(c,charMap.get(c)+1)
}else{
charMap.put(c,1)
}
}
val vector = new Array[Int](wordBag.length)
for(i <- 0 until wordBag.length){
var count=0
if(charMap.containsKey(wordBag(i))){
count=charMap.get(wordBag(i))
}else{
count=0
}
vector(i)=count
}
vector
}
override def evaluate(v1: Any, v2: Any): (String, Boolean) = {
doEvaluate(v1.asInstanceOf[String],v2.asInstanceOf[ListBuffer[String]])
}
}
写一个类,继承RiskEvaluate接口,覆盖接口方法,表示登录地是否存在异常
package com.evaluate.impl
import com.baizhi.evaluate.{Location, RiskEvaluate}
class LoginLocationEvaluate extends RiskEvaluate{
def doEvaluate(currentLogin: Location, lastLogin: Location): (String, Boolean) = {
var distance=getDistanceOfMeter(currentLogin.latitude,currentLogin.longtitude,lastLogin.latitude,lastLogin.longtitude)
var speed=distance/((currentLogin.timestamp-lastLogin.timestamp)*1.0/(1000*3600))
println("speed:"+speed)
return ("FORM_INPUT",speed>310)
}
def getDistanceOfMeter(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double = {
val a = rad(lat1) - rad(lat2)
val b = rad(lng1) - rad(lng2)
println(a+" "+b)
var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos( rad(lat1)) * Math.cos( rad(lat2)) * Math.pow(Math.sin(b / 2), 2)))
println(s)
s = s * EARTH_RADIUS
s = Math.round(s * 10000) / 10000;
return s
}
private def rad(d: Double) = d * Math.PI / 180.0
/**
* 地球半径:6378.137KM
*/
private val EARTH_RADIUS = 6378.137
override def evaluate(v1: Any, v2: Any): (String, Boolean) = {
doEvaluate(v1.asInstanceOf[Location],v2.asInstanceOf[Location])
}
}
写一个类,继承RiskEvaluate接口,覆盖接口方法,表示登陆设备是否不一致
package com.evaluate.impl
import java.util
import com.baizhi.evaluate.RiskEvaluate
import scala.collection.mutable.ListBuffer
class AgentEvaluate extends RiskEvaluate{
/**
*
* @param lastAgent:当前输入向量
* @param lastAgents:历史输入向量数组
* @return
*/
def doEvaluate(lastAgent: String, lastAgents: ListBuffer[String]): (String, Boolean) = {
return ("AGENT_INPUT",lastAgents.indexOf(lastAgent) == -1)
}
override def evaluate(v1: Any, v2: Any): (String, Boolean) = {
doEvaluate(v1.asInstanceOf[String],v2.asInstanceOf[ListBuffer[String]])
}
}
Location类
package com.evaluate
import org.apache.kafka.common.record.TimestampType
// la 纬度 lon经度 //纬度 //经度 //时间
case class Location(latitude:Double,longtitude:Double,timestamp:Long)