Android端如何简单的防黑产

如何防止薅羊毛,一直是App头疼且需要注意的问题,前端能做到真正的防黑产吗?答案是否定的,也就是说,通过前台,我们是无法知道当前用户是否是真实用户的,毕竟Android是不安全的,可以模拟用户的一系列操作手势,也可以通过抓取相应的接口,模拟相应的请求,一个黑产想要达到他想要的目的,无论程序再安全,也无非是破解难度的问题。

apk的混淆加固,相关密钥的安全存储,以及接口的加密,都是我们加深App安全的操作,也是进一步加深黑产用户的破解难度,对于数据的流失,防黑产,一般都是和接口挂钩,大部分也都是服务端需要考虑的,毕竟交互的最终结果都是传到服务端,无非前端做一些配合服务端的事情。

那么除了以上的相关处理,前端是否还有其他的简单逻辑,告知服务端,当前用户是否是真实用户呢?当然是可以的,说好了,只能是简单,纯粹的简单,不参杂一丁点的复杂思想。实现思路,其实就是针对项目中的用户最常用行为轨迹,进行埋点统计,如果触发了这个行为轨迹,就告知服务端,这是一个真实用户;这种方式只能简单的进行统计,主观意识上认为它是真实用户,并不能做到百分百,但也能起到一定的作用,比如可以定位那些暴力的刷接口行为,通过这种方式,我们就可以避免这种行为。

一个项目中的功能有很多,比如电商中的购物,及时通讯中的聊天等等,针对不同的App,肯定有用户最常用的功能,这就是大概率的用户习惯,针对这些用户习惯,我们可以设计相对应的路径,也就是用户,通过哪些事件后,进入到了最后的功能,比如购物,用户触发了商品列表,点击了商品后进入到商品详情,然后点击购买,最后发起了支付,这就是一个路径,我们可以称为是一个事件链。

事件的统计方式:

一个事件链,从开始到结束,可能有很多事件,但每一个事件链是唯一的,如下图的事件链ABC,记录好定义的事件链,就可以针对当前事件链,进行逐个事件统计,如下图中的123,到end结束,end为当前事件链结束的标识,得到结束的标识后,就得到了一个用户的执行流程,就可以上报到服务端。

举例(登录操作),定义事件链为login:

打开应用-->点击我的-->立即加入-->输入手机号-->输入验证码-->点击登录-->登录成功

以上是登录从开始到结束的整个事件链流程,针对每一个事件进行存储,除最后一个事件是end标识,前边的都为存储类型,当执行到end类型后就可以上报此事件链。

事件链的上传结果,包含用户的id,各个事件的触发标识。

具体实现:

基本的逻辑就是如上,代码也不复杂,总共可以分为三个方面,第一个是事件的统计,其实和埋点的思路一致,第二个是,针对事件链或者事件的存储,第三个就是事件的上传。

class BlackProductionUtils {
    private val mPreferences = "gwmBlack"
    private val mSpEvent = "spEvent"
    private var mSp: SharedPreferences? = null
    private var mOnce: Boolean = false//全局单次上传,所有事件链只上报一次

    companion object {
        @JvmStatic
        private var instance: BlackProductionUtils? = null
            get() {
                if (field == null) {
                    field = BlackProductionUtils()
                }
                return field
            }

        @JvmStatic
        fun get(): BlackProductionUtils {
            return instance!!
        }

    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:once:是否一个事件链只执行一次
     */
    fun init(context: Context, once: Boolean = false) {
        mOnce = once
        mSp = context.getSharedPreferences(mPreferences, 0)
    }


    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:添加事件,key为事件链标识,position为事件索引,
     * start是事件开始,end是结束事件的标识
     */
    fun addEvent(
        key: String, position: Int,
        start: Boolean = false,
        end: Boolean = false
    ) {
        try {
            //首先判断是否单次上报,如果是,取出标识,存在就中断执行
            if (mOnce && getString(mSpEvent).contains(key)) {
                return
            }
            if (start) {
                //进行清空事件,从新开始
                put(key, "")
            }
            //首先获取
            var eventData = getString(key)
            if (!TextUtils.isEmpty(eventData)) {
                //证明存在数据
                eventData += containsPosition(eventData, position)
            } else {
                //证明不存在
                eventData = position.toString()
            }

            if (end) {
                //结束,进行上报此事件
                uploadEvent(key, eventData)
            } else {
                //无结束
                put(key, eventData)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:是否包含
     */
    private fun containsPosition(eventData: String, position: Int): String {
        val p = position.toString()
        return if (!eventData.contains(p)) {
            p
        } else {
            ""
        }
    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:上报事件,key:事件链标识,eventData:一个事件链的所有事件
     */
    private fun uploadEvent(key: String, eventData: String) {
        val spEvent = getString(mSpEvent)
        if (mOnce) {
            if (!spEvent.contains(key)) {
                addSpKey(key)
                //单次进行上传
                reportEvent(key, eventData)
            }
        } else {
            //每次进行上传
            reportEvent(key, eventData)
        }

    }
    
    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:走接口,统一上报
    */
    private fun reportEvent(key: String, eventData: String) {

    }

    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:保存事件链Key
     */
    private fun addSpKey(key: String) {
        //记录每一个事件链标识
        var spKey = getString(mSpEvent)
        spKey += key
        put(mSpEvent, spKey)
    }


    private fun put(key: String, value: String) {
        try {
            mSp!!.edit().putString(key, value).commit()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun getString(key: String, defaultVal: String = ""): String {
        var mValue = ""
        mSp?.let {
            mValue = it.getString(key, defaultVal)!!
        }
        return mValue
    }


    /**
     * AUTHOR:AbnerMing
     * INTRODUCE:清除所有的事件
     */
    fun clearEvent() {
        mSp?.let {
            it.edit().clear().commit()
        }
    }
}

调用

1、初始化

默认每次事件链执行完都上传

BlackProductionUtils.get().init(this)

一个事件链只上传一次

BlackProductionUtils.get().init(this,true)

2、添加事件

默认false,不清除原有事件,true为每次事件开始时,都会重新记录事件,具体情况,比如当前事件链有10个事件,用户触发了前5个,后面不走了,隔了一段时间又从事件1开始了,之前存储的事件,清除还是保存,就是这个意思。

事件开始

BlackProductionUtils.get().addEvent("main", 0, true)

正常的事件存储,实际会有多个

BlackProductionUtils.get().addEvent("main", 1)

事件结束

BlackProductionUtils.get().addEvent("main", 3, end = true)

告知服务端调用reportEvent方法即可。

相关的逻辑就是如上代码,可以根据自己项目的存储方式,或者其他逻辑进行更改。大家可以简单的认为就是埋点,然后进行上报服务端,服务端会根据每个用户上报的事件链,来进行统计分析。

防黑产,以上的方式,开头已经陈述,并不能做到百分百,通过多个事件链,但也能够有效的过滤一些非真实用户,当然了,具体情况具体分析,你说它有意义,它就存在意义,若没意义,其实也没任何意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员一鸣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值