本文已收录github:https://github.com/BigDataScholar/TheKingOfBigData,里面有大数据高频考点,Java一线大厂面试题资源,上百本免费电子书籍,作者亲绘大数据生态圈思维导图…持续更新,欢迎star!
前言
在上一期内容中,菌哥已经为大家介绍了实时热门商品统计模块的功能开发的过程(👉基于flink的电商用户行为数据分析【3】| 实时流量统计)。本期文章,我们需要学习的是恶意登录监控模块功能的开发过程。
模块创建和数据准备
继续在UserBehaviorAnalysis
下新建一个 maven module作为子项目,命名为LoginFailDetect
。在这个子模块中,我们将会用到flink的CEP
库来实现事件流的模式匹配,所以需要在pom文件中引入CEP的相关依赖:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep-scala_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
同样,在src/main/目录下,将默认源文件目录java改名为scala。
代码实现
对于网站而言,用户登录并不是频繁的业务操作。如果一个用户短时间内频繁登录失败,就有可能是出现了程序的恶意攻击,比如密码暴力破解。因此我们考虑,应该对用户的登录失败动作进行统计,具体来说,如果同一用户(可以是不同IP)在2秒之内连续两次登录失败,就认为存在恶意登录的风险,输出相关的信息进行报警提示。这是电商网站、也是几乎所有网站风控的基本一环。
所以我们可以思考一下解决方案:
基本需求
– 用户在短时间内频繁登录失败,有程序恶意攻击的可能
– 同一用户(可以是不同IP)在2秒内连续两次登录失败,需要报警解决思路
– 将用户的登录失败行为存入 ListState,设定定时器2秒后触发,查看 ListState 中有几次失败登录
– 更加准确的检测,可以使用 CEP 库实现事件流的模式匹配
既然现在思路清楚了,那我们就尝试将方案落地。
状态编程
由于同样引入了时间,我们可以想到,最简单的方法其实与之前的热门统计类似,只需要按照用户ID分流,然后遇到登录失败的事件时将其保存在ListState中,然后设置一个定时器,2秒后触发。定时器触发时检查状态中的登录失败事件个数,如果大于等于2,那么就输出报警信息。
在src/main/scala下创建LoginFail.scala
文件,新建一个单例对象。定义样例类LoginEvent
,这是输入的登录事件流。登录数据本应该从UserBehavior日志里提取,由于UserBehavior.csv中没有做相关埋点,我们从另一个文件LoginLog.csv
中读取登录数据。
代码如下:
object LoginFailOne {
// 输入的登录事件样例类
case class LoginEvent( userId:Long,ip:String,eventType:String,eventTime:Long)
// 输出的报警信息样例类
case class Warning( userId:Long,firstFailTime:Long,lastFailTime:Long,warningMsg:String)
def main(args: Array[String]): Unit = {
// 创建流环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
// 设置并行度
env.setParallelism(1)
// 设置时间特征为事件时间
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
// 读取csv文件
env.readTextFile("G:\\LoginLog.csv")
.map(data => {
// 将文件中的数据封装成样例类
val dataArray: Array[String] = data.split(",")
LoginEvent(dataArray(0).toLong, dataArray(1), dataArray(2), dataArray(3).toLong)
})
// 设置 WaterMark 水印
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[LoginEvent](Time.seconds(5)) {
override def extractTimestamp(element: LoginEvent): Long = element.eventTime * 1000
})
// 以用户id为key,进行分组
.keyBy(_.userId)
// 计算出同一个用户2秒内连续登录失败超过2