实时业务风控系统

项目地址

代码已开源, https://github.com/sunpeak/riskcontrol

背景

当前互联网企业存在很多业务风险,有些风险(比如薅羊毛)虽然没有sql注入漏洞利用来的直接,但是一直被羊毛党、刷单党光顾的企业长期生存下来的几率会很低!

  • 账号:垃圾注册、撞库、盗号等

  • 交易:盗刷、恶意占用资源、篡改交易金额等

  • 活动:薅羊毛

  • 短信:短信轰炸

项目介绍

实时业务风控系统是分析风险事件,根据场景动态调整规则,实现自动精准预警风险的系统。

本项目只提供实时风控系统框架基础和代码模板。

需要解决的问题

  • 哪些是风险事件,注册、登录、交易、活动等事件,需要业务埋点配合提供实时数据接入

  • 什么样的事件是有风险的,风险分析需要用到统计学,对异常用户的历史数据做统计分析,找出异于正常用户的特征

  • 实时性,风险事件的分析必须毫秒级响应,有些场景下需要尽快拦截,能够给用户止损挽回损失

  • 低误报,这需要人工风控经验,对各种场景风险阈值和评分的设置,需要长期不断的调整,所以灵活的规则引擎是很重要的

  • 支持对历史数据的回溯,能够发现以前的风险,或许能够找到一些特征供参考

项目标签

  • 轻量级,可扩展,高性能的Java实时业务风控系统

  • 基于Spring boot构建,配置文件能少则少

  • 使用drools规则引擎管理风控规则,原则上可以动态配置规则

  • 使用redis、mongodb做风控计算和事件储存,历史事件支持水平扩展

原理

统计学

  • 次数统计,比如1分钟内某账号的登录次数,可以用来分析盗号等

  • 频数统计,比如1小时内某ip上出现的账号,可以用来分析黄牛党等

  • 最大统计,比如用户交易金额比历史交易都大,可能有风险

  • 最近统计,比如最近一次交易才过数秒,可能机器下单

  • 行为习惯,比如用户常用登录地址,用户经常登录时间段,可以用来分析盗号等

通用公式:某时间段,在条件维度(可以是多个维度复合)下,利用统计方法统计结果维度的值,充分发挥你的想象吧!

实时计算

要将任意维度的历史数据(可能半年或更久)实时统计出结果,需要将数据提前安装特殊结果准备好(由于事件的维度数量不固定的,选取统计的维度也是随意的,所以不是在关系数据库中建几个索引就能搞定的),需要利用空间换时间,来降低时间复杂度。

redis

redis中数据结构sortedset,是个有序的集合,集合中只会出现最新的唯一的值。利用sortedset的天然优势,做频数统计非常有利。

比如1小时内某ip上出现的账号数量统计:

  • 保存维度

    ZADD key score member(时间复杂度:O(M*log(N)), N 是有序集的基数, M 为成功添加的新成员的数量),key=ip,score=时间(比如20160807121314),member=账号。存储时略耗性能。

结构如下:

1.1.1.1

|--账号1        20160807121314

|--账号2        20160807121315

|--账号n        20160807121316


2.2.2.2

|--账号3        20160807121314

|--账号4        20160807121315

|--账号m        20160807121316
  • 计算频数

    ZCOUNT key min max(时间复杂度:O(1)),key=ip,min=起始时间,max=截止时间。计算的性能消耗极少,优势明显

  • redis lua

    把保存维度,计算频数,过期维度数据等操作,使用lua脚本结合在一起,可以减少网络IO,提供性能

mongodb

mongodb本身的聚合函数统计维度,支持很多比如:max,min,sum,avg,first,last,标准差,采样标准差,复杂的统计方法可以在基础聚合函数上建立,比如行为习惯:

getDB().getCollection(collectionName).aggregate(

  Arrays.asList(

    match(match)                                                       --匹配条件维度

    , group("$" + field, Accumulators.sum("_count", 1))                --求值维度的次数

    , match(new Document("_count", new Document("$gte", minCount)))   --过滤,超过minCount才统计

    , sort(new Document("_count", -1))                                --对次数进行倒叙排列

  )

);

建议在mongodb聚合的维度上建立索引,这样可以使用内存计算,速度较快。

redis性能优于mongodb,所以使用场景较多的频数计算默认在redis中运行,参考代码DimensionService.distinctCountWithRedis方法。但是redis为了性能牺牲了很多空间,数据重复存储,会占用很多内存。

环境准备

  • mysql,数据结构在import.sql中定义了

  • redis

  • mongodb,建议使用分片集群

项目配置

  • 应用配置:application.properties

  • 日志配置:logback.xml

  • 规则配置:rules/*.drl,规则都是用java语言编写。默认配置了登录事件的部分规则

单个规则文件说明:

package rules;                                        --规则包路径

import com.example.riskcontrol.model.LoginEvent        --引入类

import com.example.riskcontrol.service.DimensionService

import com.example.riskcontrol.model.EnumTimePeriod

global DimensionService dimensionService             --引入外部服务

rule "98_login_ip"                                   --规则名称,全局唯一

  salience 98                                        --规则优先级,值越大越先执行

  lock-on-active true                                --事件不重复执行该规则

when                                                 --条件判断,是否需要进入action

  event:LoginEvent()                                --判断事件对象是否是LoginEvent类

then                                                --action

  int count  = dimensionService.distinctCount(event,new String[]{LoginEvent.OPERATEIP},EnumTimePeriod.LASTHOUR,LoginEvent.MOBILE);        --近1小时内该事件ip上出现的mobile数量统计

  if(event.addScore(count,20,10,1)){        --如果统计结果超过20个,则记10分,并且结果每超1个,再多记1分

    dimensionService.insertRiskEvent(event,"近1小时内同ip出现多个mobile,count="+count);  --记录风险事件日志

  }

end       --结束规则

drools的详细文档,请参考官方http://docs.jboss.org/drools/release/6.4.0.Final/drools-docs/html_single/index.html

部署

系统默认采用jar打包和运行,建议集群方式部署,然后使用反向代理做负载均衡。

打包

mvn clean install

运行

建议jdk 8

java -jar riskcontrol-*.jar

war包部署

如果需要tomcat等容器部署,也可将配置打包方式修改成war包方式,修改pom.xml

 <packaging>war</packaging>

风控分析入口

TODO

  • 扩展黑白名单,ip,手机号,设备指纹等;

  • 扩展维度信息,比如手机号地域运营商,ip地域运营商,ip出口类型,设备指纹,Referer,ua,密码hash,征信等,维度越多,可以建立规则越多,风控越精准;

  • 扩展风控规则,针对需要解决的场景问题,添加特定规则,分值也应根据自身场景来调整。

  • 将用户的行为轨迹综合考虑,建立复合场景的规则条件。比如:登录->活动->订单->支付,将事件关联分析综合考虑;

  • 减少漏报和误报。当然,这将是个漫长的过程;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容