用户登录风险评估
参考https://zhuanli.tianyancha.com/796c88e9c3d67d3a9c59716558818e22
-检查出⾮⽤户登录,或者账号被盗窃等潜在⻛险挖掘。通过对⽤户登录⾏为进⾏分析,提⾼了预测的准
确性;可以应⽤于互联⽹⾦融⻛控技术中,也可应⽤于普通⽹站的⽤户恶意登录识别技术中。
有以下几种情况:
- 异地登录认定有⻛险(不在⽤户经常登陆地)
- 登录的位移速度超过正常值,认定有⻛险
- 更换设备,认定有⻛险
- 登录时段-习惯,出现了异常,存在⻛险
- 每天的累计登录次数超过上限,认定有⻛险
- 密码输⼊错误或者输⼊差异性较⼤,认定⻛险
- 如果⽤户的输⼊特征(输⼊每个控件所需时⻓ ms)发⽣变化
算法实现概述
⼀种基于⽤户登录⾏为分析的⻛控⽅法,其特征在于:所述的⽅法包括 ⽤户按键⻛险识别
、⽤户登录地 ⻛险识别
、密码重试⻛险识别
、 设备来源⻛险识别
、 ⽤户登录习惯⻛险识别
、 累计登录多次⻛险识 别
、 ⽤户登录的瞬时位移速度⻛险识别
七种模型;本系统仅仅负责根据⽤户的登录数据产⽣⻛险评估报
告。报告的格式为:
并不负责对⽤户的登录做出定性的分析,仅仅当系统发送⽤户登录数据过来,由⼤数据评估系统对⽤户
数据进⾏评估,然后产⽣评估报告。由业务系统⾃⾏根据评估报告,需⽤⽤户采取相关的奖惩措施。介
于以上评估报告系统需要⽤户发送以下数据格式,以辅助系统完成验证:
需要评估登录数据:⽤于本次登录前产⽣评估报告的数据,系统拿到评估报告以后,再去做抉择是否升级登录。该数据需要获取⽤户的 历史登录成功的数据集合
完成评估!
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"
登录成功产⽣数据:登录成功的数据 ⽤来作为下⼀次评估登录时的历史数据,系统留存最近的⼀些历
史登录数据集,作为下⼀次登录评估的标准
INFO 2020-03-31 10:12:00 QQ SUCCESS [张三] 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"
数据抓取
目标日志信息
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"
数据抓取的正则表达式
^INFO\s(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s([a-z0-9\u4e00-\u9fa5]*)\s(EVALUATE|SUCCESS)\s\[([a-z0-9\u4e00-\u9fa5]*)\]\s([a-z0-9]{32})\s\"([a-z0-9\.\-\,]{6,12})\"\s([a-z\u4e00-\u9fa5]*)\s\"([0-9\.\,]*)\"\s\[([0-9\,\.]*)\]\s\"(.*)\"
借助https://regex101.com/
工具进行测试
构建三个实体类
- 1.用户登录前产⽣评估报告的数据
@Data
@AllArgsConstructor
@NoArgsConstructor
//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"
public class EvaluateData implements Serializable {
private long evaluateTime;//注册时间戳
private String applicationName;//应用名
private String userIdentify;//用户名标识
private String loginSequence;//用户uuid
private String ordernessPassword;//乱序之后的密码
private String cityName;//城市名
private GeoPoint geoPoint;//经纬度
private Double[] inputFeatures;//用户登陆输入特征(用户名输入时间,密码输入时间,验证码输入时间)
private String deviceInformation;//用户登录设备信息
}
经纬度
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GeoPoint implements Serializable {
private double longtitude;//经度
private double latitude;//纬度
}
- 2.用户登录成功的数据
@Data
@AllArgsConstructor
@NoArgsConstructor
//INFO 2020-03-31 10:12:00 QQ SUCCESS [张三] 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"
public class LoginSuccessData implements Serializable {
private long evaluateTime;
private String applicationName;
private String userIdentify;
private String loginSequence;
private String ordernessPassword;
private String cityName;
private GeoPoint geoPoint;
private Double[] inputFeatures;
private String deviceInformation;
}
- 3.用户进几次登陆成功的历史数据
在这里插入代码片
java.util.regex包下的Pattern和Matcher用法用例(正则匹配)
java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现。
Pattern类用于创建一个正则表达式,也可以说创建一个匹配模式,它的构造方法是私有的,不可以直接创建,但可以通过 Pattern.complie(String regex) 简单工厂方法创建一个正则表达式。
Pattern p=Pattern.compile("\\w+");
p.pattern();//返回 \w+
compile(String regex) 将正则表达式编译到Pattern中。该方法是个静态方法;
pattern() 返回正则表达式的字符串形式,其实就是返回Pattern.complile(String regex)的regex参数 。
- 1.Pattern.split(CharSequence input)
Pattern有一个split(CharSequence input)方法,用于分隔字符串,并返回一个String[]
Pattern p=Pattern.compile("\\d+"); //数字匹配分割
String[] str=p.split("我的姓名是:456456我的电话是:0532214我的邮箱是:aaa@aaa.com");
结果:str[0]=“我的姓名是:” str[1]=“我的电话是:” str[2]=“我的邮箱是:aaa@aaa.com”
- 2.Pattern.matcher(String regex,CharSequence input)
Pattern.matcher(String regex,CharSequence input)是一个静态方法,用于快速匹配字符串,该方法适合用于只匹配一次,且匹配全部字符串;
Pattern.matches("\\d+","2223");//返回true
Pattern.matches("\\d+","2223aa");//返回false,需要匹配到所有字符串才能返回true,这里aa不能匹配到
Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,这里bb不能匹配到
- 3.Pattern.matcher(CharSequence input)
Pattern.matcher(CharSequence input)返回一个Matcher对象.
Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例.
Pattern类只能做一些简单的匹配操作,要想得到更强更便捷的正则匹配操作,那就需要将Pattern与Matcher一起合作.Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持.
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.pattern();//返回p 也就是返回该Matcher对象是由哪个Pattern对象的创建的
- 4.Matcher.matches()
Matcher类提供三个匹配操作方法(Matcher.matches()/ Matcher.lookingAt()/ Matcher.find() ),三个方法均返回boolean类型,当匹配到时返回true,没匹配到则返回false
matches()对整个字符串进行匹配,只有整个字符串都匹配了才返回true
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.matches();//返回false,因为bb不能被\d+匹配,导致整个字符串匹配未成功.
Matcher m2=p.matcher("2223");
m2.matches();//返回true,因为\d+匹配到了整个字符串
Pattern.matcher(String regex,CharSequence input)等价 Pattern.compile(regex).matcher(input).matches()
- 5.Matcher.lookingAt()
lookingAt()对前面的字符串进行匹配,只有匹配到的字符串在最前面才返回true
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.lookingAt();//返回true,因为\d+匹配到了前面的22
Matcher m2=p.matcher("aa2223");
m2.lookingAt();//返回false,因为\d+不能匹配前面的aa
- 6.Matcher.find()
find()对字符串进行匹配,匹配到的字符串可以在任何位置.
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.find();//返回true
Matcher m2=p.matcher("aa2223");
m2.find();//返回true
Matcher m3=p.matcher("aa2223bb");
m3.find();//返回true
Matcher m4=p.matcher("aabb");
m4.find();//返回false
- 7.Mathcer.start()/ Matcher.end()/ Matcher.group()
当使用matches(),lookingAt(),find()执行匹配操作后
,就可以利用以下三个方法得到更详细的信息.
start():返回匹配到的子字符串在字符串中的索引位置.
end():返回匹配到的子字符串的最后一个字符在字符串中的索引位置.
group():返回匹配到的子字符串
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("aaa2223bb");
m.find();//匹配2223
m.start();//返回3
m.end();//返回7,返回的是2223后的索引号
m.group();//返回2223
Mathcer m2=p.matcher("2223bb");
m2.lookingAt(); //匹配2223
m2.start(); //返回0,由于lookingAt()只能匹配前面的字符串,所以当使用lookingAt()匹配时,start()方法总是返回0
m2.end(); //返回4
m2.group(); //返回2223
Matcher m3=p.matcher("2223"); //如果Matcher m3=p.matcher("2223bb"); 那么下面的方法出错,因为不匹配返回false
m3.matches(); //匹配整个字符串
m3.start(); //返回0
m3.end(); //返回3,原因相信大家也清楚了,因为matches()需要匹配所有字符串
m3.group(); //返回2223
start(),end(),group()均有一个重载方法它们是start(int i),end(int i),group(int i)专用于分组操作,Mathcer类还有一个groupCount()用于返回有多少组.
Pattern p=Pattern.compile("([a-z]+)(\\d+)");
Matcher m=p.matcher("aaa2223bb");
m.find(); //匹配aaa2223 ,find是匹配任意位置
m.groupCount(); //返回2,因为有2组
m.start(1); //返回0 返回第一组匹配到的子字符串在字符串中的索引号
m.start(2); //返回3
m.end(1); //返回3 返回第一组匹配到的子字符串的最后一个字符在字符串中的索引位置.
m.end(2); //返回7
m.group(1); //返回aaa,返回第一组匹配到的子字符串
m.group(2); //返回2223,返回第二组匹配到的子字符串
例子:
例如有一段文本(我的QQ是:3609789 我的电话是:0532214 我的邮箱是:aaa123@aaa.com),里面有很多数字,而且这些数字是分开的,我们现在要将文本中所有数字都取出来,利用java的正则操作:
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("我的QQ是:3609789 我的电话是:0532214 我的邮箱是:aaa123@aaa.com");
while(m.find()) {
System.out.println(m.group());
}
输出:
3609789
0532214
123
小结
1.每次执行匹配操作后start(),end(),group()三个方法的值都会改变,变成匹配到的子字符串的信息,以及它们的重载方法,也会改变成相应的信息.
2.只有当匹配操作成功,才可以使用start(),end(),group()三个方法,否则会抛出java.lang.IllegalStateException,也就是当matches(),lookingAt(),find()其中任意一个方法返回true时,才可以使用.
3.Pattern与Matcher一起合作,Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持.。单独用Pattern只能使用Pattern.matcher(String regex,CharSequence input)一种最基础最简单的匹配。
构建工具类EvaluateUtil
package com.baizhi.util;
import com.baizhi.entities.EvaluateData;
import com.baizhi.entities.GeoPoint;
import com.baizhi.entities.LoginSuccessData;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EvaluateUtil {
public static final String LEGAL_REGEX="^INFO\\s(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2})\\s([a-z0-9\\u4e00-\\u9fa5]*)\\s(EVALUATE|SUCCESS)\\s\\[([a-z0-9\\u4e00-\\u9fa5]*)\\]\\s([a-z0-9]{32})\\s\\\"([a-z0-9\\.\\-\\,]{6,12})\\\"\\s([a-z\\u4e00-\\u9fa5]*)\\s\\\"([0-9\\.\\,]*)\\\"\\s\\[([0-9\\,\\.]*)\\]\\s\\\"(.*)\\\"";
public static final String EVALUATE_REGEX="^INFO\\s(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2})\\s([a-z0-9\\u4e00-\\u9fa5]*)\\s(EVALUATE)\\s\\[([a-z0-9\\u4e00-\\u9fa5]*)\\]\\s([a-z0-9]{32})\\s\\\"([a-z0-9\\.\\-\\,]{6,12})\\\"\\s([a-z\\u4e00-\\u9fa5]*)\\s\\\"([0-9\\.\\,]*)\\\"\\s\\[([0-9\\,\\.]*)\\]\\s\\\"(.*)\\\"";
public static final String SUCCESS_REGEX="^INFO\\s(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2})\\s([a-z0-9\\u4e00-\\u9fa5]*)\\s(SUCCESS)\\s\\[([a-z0-9\\u4e00-\\u9fa5]*)\\]\\s([a-z0-9]{32})\\s\\\"([a-z0-9\\.\\-\\,]{6,12})\\\"\\s([a-z\\u4e00-\\u9fa5]*)\\s\\\"([0-9\\.\\,]*)\\\"\\s\\[([0-9\\,\\.]*)\\]\\s\\\"(.*)\\\"";
public static final Pattern LEGAL_PATTERN = Pattern.compile(LEGAL_REGEX, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
public static final Pattern EVALUATE_PATTERN = Pattern.compile(EVALUATE_REGEX, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
public static final Pattern SUCCESS_PATTERN = Pattern.compile(SUCCESS_REGEX, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
public static Boolean isLegal(String input){
Matcher matcher = LEGAL_PATTERN.matcher(input);
return matcher.matches();
}
public static Boolean isEvaluate(String input){
Matcher matcher = EVALUATE_PATTERN.matcher(input);
return matcher.matches();
}
public static Boolean isLoginSuccess(String input){
Matcher matcher = SUCCESS_PATTERN.matcher(input);
return matcher.matches();
}
public static EvaluateData parseEvaluateData(String input) throws ParseException {
EvaluateData evaluateData = null;
//判断是否是Evaluate
if(isEvaluate(input)){
Matcher matcher = EVALUATE_PATTERN.matcher(input);
boolean b = matcher.find();
System.out.println("b:"+b);
int i = matcher.groupCount();
System.out.println("i:" + i);
String strDate = matcher.group(1);//拿到时间字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long evaluateTime = sdf.parse(strDate).getTime();//拿到时间戳
String applicationName = matcher.group(2);//拿到应用名
String userIdentify = matcher.group(4);//拿到用户标识-用户名
String loginSequence = matcher.group(5);//用户uuid
String ordernessPassword = matcher.group(6);//乱序后的密码
String cityName = matcher.group(7);//城市
String[] geoArray = matcher.group(8).split(",");//经纬度数组
GeoPoint geoPoint = new GeoPoint(Double.valueOf(geoArray[0]),Double.valueOf(geoArray[1]));//经纬度
String inputFeature = matcher.group(9);//输入特性
String[] feaArray = inputFeature.split(",");
Double[] inputFeatures = {Double.valueOf(feaArray[0]),Double.valueOf(feaArray[1]),Double.valueOf(feaArray[2])};//用户输入特性
String deviceInformation = matcher.group(10);//设备信息
//构建evaluateData
evaluateData = new EvaluateData(evaluateTime,applicationName,userIdentify,loginSequence,ordernessPassword,cityName,geoPoint,inputFeatures,deviceInformation);
}
return evaluateData;
}
public static LoginSuccessData parseLoginSuccessData(String input) throws ParseException {
LoginSuccessData loginSuccessData = null;
//判断是否是Evaluate
if(isLoginSuccess(input)){
Matcher matcher = SUCCESS_PATTERN.matcher(input);
boolean b = matcher.find();
System.out.println("b:"+b);
int i = matcher.groupCount();
System.out.println("i:" + i);
String strDate = matcher.group(1);//拿到时间字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long evaluateTime = sdf.parse(strDate).getTime();//拿到时间戳
String applicationName = matcher.group(2);//拿到应用名
String userIdentify = matcher.group(4);//拿到用户标识-用户名
String loginSequence = matcher.group(5);//用户uuid
String ordernessPassword = matcher.group(6);//乱序后的密码
String cityName = matcher.group(7);//城市
String[] geoArray = matcher.group(8).split(",");//经纬度数组
GeoPoint geoPoint = new GeoPoint(Double.valueOf(geoArray[0]),Double.valueOf(geoArray[1]));//经纬度
String inputFeature = matcher.group(9);//输入特性
String[] feaArray = inputFeature.split(",");
Double[] inputFeatures = {Double.valueOf(feaArray[0]),Double.valueOf(feaArray[1]),Double.valueOf(feaArray[2])};//用户输入特性
String deviceInformation = matcher.group(10);//设备信息
//构建evaluateData
loginSuccessData = new LoginSuccessData(evaluateTime,applicationName,userIdentify,loginSequence,ordernessPassword,cityName,geoPoint,inputFeatures,deviceInformation);
}
return loginSuccessData;
}
public static void main(String[] args) {
String input="INFO 2020-03-31 10:12:00 Q1Q应用1 success [张三] 6ebaf4ac780f40f486359f3ea6934620 \"123456\" 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\"";
System.out.println(EvaluateUtil.isLegal(input));
}
}