赞
踩
本文将向大家展示低代码开发:通过使用用户组件鉴权避免非微信用户获取红包,并使用服务和事务记录用户申领红包的记录;符合获取红包的用户,则通过后台逻辑发送后台生成的金额至本人微信中。
iVX是一个 “零代码” 的可视化编程语言,拥有方便的在线集成开发环境 ,不需要下载开发环境,打开浏览器即可随时随地的进行项目编辑。
今天,我们来专门讨论一下微信红包的应用的做法。我们在做红包应用的时候,最常见会出现的问题是:
1、被刷
2、并发过高,产生过高费用
我们先来专门分析一下这两个问题的产生原因,然后通过一个demo案例,来给出一种相对合理的解决方法。
首先,被刷。被羊毛党恶意刷红包,主要有两种方式,第一种是破解了前端的加密请求后,设置固定微信的openid,向我们的服务器发送领取红包的请求,直接将红包发送给预定的openid。第二种是,将红包地址发到专门的“羊毛群”,然后由真实的羊毛党用户手动领取红包。请注意,如果我们的红包领取没有设置任何规则,比如需要通过某个预先发放的券码来兑换,任何有效的微信用户都可以领取红包的话,那第二种真实用户的“羊毛群”是无法防的,我们只能在活动时做好保密工作,并适当限制并发,限制羊毛党领取红包的速度,给我们留下应对的事件。(注意,仅仅是在微信里禁用转发,并不能防止羊毛党将红包链接发出去,因为一个H5的地址有很多种方法可以获取到,比如直接复制URL地址。)
因此,针对第二种真实用户的羊毛群,我们强烈建议在设计红包活动的时候,要设置一定的门槛,比如,用户必须先关注公众号,在公众号后台领取一个红包口令,然后通过这个口令,可以兑换真实的红包。这样,即使是真实用户,也必须要先关注公众号。或者,预先设置一批可以领取红包的用户名单,在领取的时候进行验证是否在这个白名单中。
接下来,我们重点来分析一下怎样防第一种类型的刷红包。相比第二种真实用户,第一种的危害性更大,因为刷红包的人可以预先申请好一批僵尸号,直接通过后台服务来批量把红包刷到这些僵尸号中。这种批量刷,可能在瞬间就把几千几万块的红包刷走了。而相对而言,第二种真实的羊毛群刷法,不太可能出现瞬间几秒钟就把红包全部刷完的情况,因为真实用户还是必须要完整走完我们的前端展示流程,到最后一步才能领红包。尽管他们不是我们客户的"目标用户",但依然是人类用户,而不是机器。
防止第一种机器刷的方法,主要是为我们的服务设置防线:
ivx的系统前端,会自动对请求的信息进行加密,我们可以在network里看到具体的请求信息,在案例预览中,可以看到请求的明码,比如:
同样的请求,在发布时,就是加密后的数据了:
因此,正常情况下,通过查看发布后的H5,是无法获取真实的请求数据,并模拟发送请求的。但请注意,前端的加密,由于是在客户端使用js来进行,从理论上来说,还是有可能被高手破解,并获取到请求的原始信息。因此,我们不能依赖前端加密,必须要在服务后台建立更多的防线。
PS:iVX平台正在开发升级版本的前端加密方法,届时,请求加密部分会使用web assembly而不是纯js,破解前端加密的难度会成倍提升。但即使这样,我们依然不能把完全信赖客户端的加密。
任何不需要鉴权的服务都是危险的。通常,我们需要用户先登录某一个账号,然后才有权限调用服务。比如,我们查看ivx工作台里的作品列表,不可能允许未登录的用户,去查看任意其他用户的作品。我们在自己制作服务的时候也一样,必须在服务层面要求用户权限。
在我们的红包场景中,我们设置了三个服务,这三个服务,必须是用户登录为“普通用户”之后,才可以调用,否则,就会返回无权限:
同时,用户在初始化,需要在前端发起微信登录:
登录的过程,其实是ivx的后台将返回一个token,存储在用户手机端浏览器的cookie里。当用户在客户端调用任何服务时,都会自动将这个token发送给后台服务(这个过程是自动完成,我们不需要手动操作),因此如果没有登录过用户组件,获取到合法的token,就没有权限调用服务了。另外,由于发起微信登录,需要在微信客户端和后台配合调用一系列的鉴权流程,没有一个合法的微信账号,并在微信客户端发起,基本是不可能登录成功,因此,在这一步,我们就有效的设置了第二道防线,让机器刷的服务请求无法顺利通过验证了。
另外,要为服务设置权限,前提是必须要有实体的服务。因此,在红包应用中,请严格使用后台服务来发送红包,而不是直接在前端调用红包组件,依赖系统生成的自动服务。
尽管以上第二层的防线,基本可以防止机器刷了。但我们还是尽量再设置第三道防线,以防万一。比如,羊毛党,可以破解了前端的服务加密,并使用自动化的N个手机设备,自动刷红包。每个手机设备,都登录一个真实的微信账号,或者都有一个有效的手机号,可以自动识别验证码,并注册。
比如类似以上的设备集合。尽管雇佣这种专业“设备农场”,成本会比较高,但只要利益足够,依然会有人去做。在这种情况下,我们就需要进一步保护我们的服务了。保护服务的一个最简单的原则,就是尽量把逻辑都放在后台,不要使用前端的参数。比如说,我们的领取红包服务,里面有两个重要的参数,红包的金额,以及发送红包的目标openid。如果这两个参数,我们都让前端在调用服务的时候发过来,那一旦服务被破解,羊毛党就可以调用“设备农场”,用任意号进行登录,然后指定金额(可以是我们允许的最大金额),以及目标用户,很快就可以把红包全部刷到指定的账号下。
但如果,我们领取红包服务,不暴露任何参数,用户是否可以领取红包,领取多少,都是在后台逻辑里判断,那即使羊毛党开刷,也无法指定账号和金额,大大减少了刷红包的收益,提高了难度:
我们还可以额外使用AI验证,来判断某个行为是人类操作还是机器人操作。目前阿里云、腾讯云都有类似的验证方法,建议针对敏感的应用,添加上类似的验证,这样不仅可以防止机器人调用服务刷红包,还可以让羊毛党“望而却步”,放弃刷服务,来降低我们的成本。
接下来我们来解决第二个问题,即如果尽可能的减少抢红包时的服务器压力,以提高服务响应效率,减少服务费用。在红包应用中,最大的一个消耗,即用数据库判断一个用户只能领取一次红包,或者需要满足一定条件之后,才能满足领取红包。
首先,我们在红包组件中,可以限制每人的领取次数,通常,每人只能领取一次:
如果我们的应用是直接领取红包,那除了限制红包领取服务的调用频率(通过高并发限制,默认是100,可以手动联系客服调低),也没有其他可以优化的地方了。但通常的活动规则,我们可能会限制用户领到红包的概率,举个例子,每人只能申请领取一次,有一定的概率,可以领到红包,还有一定的概率,没有中奖。但无论是否中奖,都无法再次申请抽奖了。
因此,我们会在红包组件的基础上,再做一个申请记录表,这个申请记录表的提交限制,直接使用提交用户的独立唯一索引:
这样,就可以限制每个用户只能像这个表中提交一条记录。如果,我们要进一步限制每个用户每天只能领取一次,还可以额外添加一个日期字段,并添加一个数据唯一的联合索引。
以上,我们就通过独立唯一索引,实现了限制用户总共只能提交一次申请,或每日每天只能提交一次申请的方法。这种方法,相对数据库的提交限制面板中的限制,效率更高:
在以上提交限制面板中,填写的每人提交限制,是iVX内部通过数据库事务实现的,事务相对普通的服务,由于会锁定记录,会消耗更多的数据库服务时间。
因此,如果我们的限制是每人总共或每人每日只能提交一次申请,那强烈建议使用数据唯一索引,或数据唯一的联合索引。同时,尽量避免使用数据输出/统计等方法,对用户的申请次数进行统计,然后根据统计结果返回用户是否可以再次申请红包。这种方法,除了额外的消耗,可能还会在高并发时造成数据的不一致性,给羊毛党可趁之机。
除了尽可能优化限制用户申请抽奖次数的方法,红包应用还容易有一个误区,就是有些用户,会根据不同的红包金额,每个创建一个红包组件,或者一个申请记录数据库。注意,红包组件,本身是自带一个数据库记录的。而在一个案例中创建多个数据库,会消耗额外的资源,并且不好管理。因此,我们完全可以使用后台逻辑,来动态生成红包金额,而不需要为每一种红包金额,创建一个红包组件。
根据以上原则,我们做一个简单红包例子。
先介绍一下这个红包例子中的游戏规则,大家可以了解实现方法后,根据自己的实际需求,来调整规则:
- 每个用户只能申请领取一次红包,申请时,顺便提供姓名、手机等信息;
- 每个用户申请之后,有一定的概率会中奖,一定的概率不中奖。如果不中奖,则不再有机会再次申请;
- 用户如果中奖,后台会规矩一定的规则,分配一个红包金额,然后像用户发红包。在我们的例子中,这个规则比较简单,会随机在4种金额:1、2、5、10中挑选一个
最后,我们开启了红包验证码,为了防机器刷,我们限制了每个用户尝试输入验证码,只能三次,如果超过这个次数,就不能再尝试了。
接下来,我们来讲一下制作的重点:
后台带数据库的组件,我们一共加了三个,分别是红包组件(自带数据库),用户组件(自带数据库),申领记录(一个私有数据库组件):
其中,用户表,记录每个参与活动的微信用户,在案例前端初始化时,发起微信登录。用户表的作用是限制服务权限,这个我们在第二节中已经详细介绍了。申领记录,记录了用户在申请时提交的信息,以及三个额外的逻辑字段:
其中,金额代表该用户领到的金额,-1代表已经抽中,但尚未领取,或者由于某种原因(比如微信账号中余额不足)而领取失败;是否抽中,代表这个用户是否抽中了红包,1即抽中,0即没有抽中。剩余验证码,记录当前用户剩余的可以尝试验证码的次数,如果减到0,则他无法再申请新的红包验证码。
最后,这个申请记录数据库,为“提交用户”字段,设置了独立数据唯一索引,在提交时就限制了每个openid只能提交一次。
这个红包应用中,我们设计了三个服务:
把预定的金额,填在一个后台的一维数组里,然后通过后台的随机抽取一个值,来决定这次红包的发送金额。
以上,我们在后台服务中随机获取一个红包金额,然后传递给红包事务。
在红包事务中,我们真正去调用红包接口。之所以要使用事务,是因为我们希望严格的在我们的申领记录表中,记录每个领到红包的用户最终领到的真实金额。因此,我们先对申领表进行更新:
注意,这个更新,除了是一个数据更新操作之外,还另外起到了鉴权保护作用。即我们必须要求在数据库中,已经存在了当前用户的申请记录,且字段“是否抽中”为1, 否则,如果更新条数<1, 说明数据库中并不存在符合条件的记录,这个领取红包,就是非法的,我们直接返回错误。
如果第一步更新验证成功,那我们就调用红包组件发红包:
但请注意,由于红包接口不一定能调用成功,比如由于网络问题,或者公众号没钱了,可能存在用户已经更新了领取金额记录,但实际上没有领取到红包的情况。因此,我们在发送红包失败的情况下,做了事务的回滚,取消掉第一步的更新操作,让当前用户的领取金额,恢复到-1。
在重发验证码服务中,我们会将这个剩余验证码减1, 然后返回一个验证码:
如果更新失败,说明数据库中并没有符合条件的记录,说明验证码次数已超过3次,就直接返回报错。注意,这里对剩余验证码减一的逻辑,和抽奖/预约的逻辑是一样的,我们不要先去查询到符合条件的记录,再减一,而是把查询条件和更新操作合二为一,以保证数据一致性,并减少服务消耗。同时,我们注意到,我们并没有为“剩余验证码”字段添加索引,原因是前面两个筛选条件,足以定位到数据库里的精确数据,最后一个"剩余验证码>1"的条件,不需要用来定位数据,只是用来验证数据有效性。因此,不需要为它额外浪费索引资源。
最后,服务都完成之后,我们在前端自动刷新验证吗:
如上,在点击领取红包,如果失败原因是“图片验证码错误”,则直接调用重发验证码服务。
整个使用下来 ,我发现iVX真的做到了快速学习、快速开发、快速运行、快速维护,对新手真的友好,作为开发者的角度,永远是动手>理解的,所以说,大家可以大胆尝试下哦。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。