安全兜底:涉及钱时短信必须考虑防刷、限量和防重

开放平台资源的使用需要考虑防刷

短信验证码服务属于开放性服务,由用户侧触发,且因为是注册验证码所以不需要登录就可以使用,很容易被短信轰炸平台利用

@GetMapping("wrong")
public void wrong() {sendSMSCaptcha("13600000000");
}private void sendSMSCaptcha(String mobile) {//调用短信通道
}

对于短信验证码这种开放接口,程序逻辑内需要有防刷逻辑。

1. 控制相同手机号的发送次数和发送频次: 

限制同一手机号每天的最大请求次数, 频率

2. 增加前置图形验证码: 

短信轰炸平台一般会收集很多免费短信接口,一个接口只会给一个用户发一次短信,所以控制相同手机号发送次数和间隔的方式不够有效, 即将弹出图形验证码作为前置

虚拟资产并不能凭空产生无限使用

虚拟资产虽然是平台方自己生产和控制,但如果生产出来可以立即使用就有立即变现。比如,因为平台 Bug 有大量用户领取高额优惠券,并立即下单使用。

在商家看来,这很可能只是一个用户支付的订单,并不会感知到用户使用平台方优惠券的情况;同时,因为平台和商家是事后结算的,所以会马上安排发货。而发货后基本就不可逆了,一夜之间造成了大量资金损失。

我们从代码层面模拟一个优惠券被刷的例子。

假设有一个 CouponCenter 类负责优惠券的产生和发放。如下是错误做法,只要调用方需要,就可以凭空产生无限的优惠券:

@Slf4j
public class CouponCenter {//用于统计发了多少优惠券AtomicInteger totalSent = new AtomicInteger(0);public void sendCoupon(Coupon coupon) {if (coupon != null)totalSent.incrementAndGet();}public int getTotalSentCoupon() {return totalSent.get();}//没有任何限制,来多少请求生成多少优惠券public Coupon generateCouponWrong(long userId, BigDecimal amount)              {return new Coupon(userId, amount);}
}

这样一来,使用 CouponCenter 的 generateCouponWrong 方法,想发多少优惠券就可以发多少:

@GetMapping("wrong")
public int wrong() {CouponCenter couponCenter = new CouponCenter();//发送10000个优惠券IntStream.rangeClosed(1, 10000).forEach(i -> {Coupon coupon = couponCenter.generateCouponWrong(1L, new BigDecimal("100"));couponCenter.sendCoupon(coupon);});return couponCenter.getTotalSentCoupon();
}

更合适的做法是,把优惠券看作一种资源,其生产不是凭空的,而是需要事先申请

接下来,我们按照这个思路改进一下程序。

首先,定义一个 CouponBatch 类,要产生优惠券必须先向运营申请优惠券批次,批次中包含了固定张数的优惠券、申请原因等信息:

//优惠券批次
@Data
public class CouponBatch {private long id;private AtomicInteger totalCount;private AtomicInteger remainCount;private BigDecimal amount;private String reason;
}

在业务需要发放优惠券的时候,先申请批次,然后再通过批次发放优惠券:

@GetMapping("right")
public int right() {CouponCenter couponCenter = new CouponCenter();//申请批次    CouponBatch couponBatch = couponCenter.generateCouponBatch();IntStream.rangeClosed(1, 10000).forEach(i -> {Coupon coupon = couponCenter.generateCouponRight(1L, couponBatch);//发放优惠券couponCenter.sendCoupon(coupon);});return couponCenter.getTotalSentCoupon();
}

可以看到,generateCouponBatch 方法申请批次时,设定了这个批次包含 100 张优惠券。在通过 generateCouponRight 方法发放优惠券时,每发一次都会从批次中扣除一张优惠券,发完了就没有了:

public Coupon generateCouponRight(long userId, CouponBatch couponBatch) {if (couponBatch.getRemainCount().decrementAndGet() >= 0) {return new Coupon(userId, couponBatch.getAmount());} else {log.info("优惠券批次 {} 剩余优惠券不足", couponBatch.getId());return null;}
}public CouponBatch generateCouponBatch() {CouponBatch couponBatch = new CouponBatch();couponBatch.setAmount(new BigDecimal("100"));couponBatch.setId(1L);couponBatch.setTotalCount(new AtomicInteger(100));couponBatch.setRemainCount(couponBatch.getTotalCount());couponBatch.setReason("XXX活动");return couponBatch;
}

这样改进后的程序,一个批次最多只能发放 100 张优惠券:在通过 generateCouponRight 方法发放优惠券时,每发一次都会从批次中扣除一张优惠券,发完了就没有了

钱的进出一定要和订单挂钩并且实现幂等

涉及钱的进出,需要做好以下两点。

第一,任何资金操作都需要在平台侧生成业务属性的订单,可以是优惠券发放订单,可以是返现订单,也可以是借款订单,一定是先有订单再去做资金操作。

第二,一定要做好防重,也就是实现幂等处理,并且幂等处理必须是全链路的。这里的全链路是指,从前到后都需要有相同的业务订单号来贯穿,实现最终的支付防重。

关于这两点,你可以参考下面的代码示例:

//错误:每次使用UUID作为订单号
@GetMapping("wrong")
public void wrong(@RequestParam("orderId") String orderId) {PayChannel.pay(UUID.randomUUID().toString(), "123", new BigDecimal("100"));
}//正确:使用相同的业务订单号
@GetMapping("right")
public void right(@RequestParam("orderId") String orderId) {PayChannel.pay(orderId, "123", new BigDecimal("100"));
}
//三方支付通道
public class PayChannel {public static void pay(String orderId, String account, BigDecimal amount) {...}
}

对于支付操作,我们一定是调用三方支付公司的接口或银行接口进行处理的。一般而言,这些接口都会有商户订单号的概念,对于相同的商户订单号,无法进行重复的资金处理,所以三方公司的接口可以实现唯一订单号的幂等处理。

但是,业务系统在实现资金操作时容易犯的错是,没有自始至终地使用一个订单号作为商户订单号,透传给三方支付接口。出现这个问题的原因是,比较大的互联网公司一般会把支付独立一个部门。支付部门可能会针对支付做聚合操作,内部会维护一个支付订单号,然后使用支付订单号和三方支付接口交互。最终虽然商品订单是一个,但支付订单是多个,相同的商品订单因为产生多个支付订单导致多次支付。

如果说,支付出现了重复扣款,我们可以给用户进行退款操作,但给用户付款的操作一旦出现重复付款,就很难把钱追回来了,所以更要小心。

这,就是全链路的意义,从一开始就需要先有业务订单产生,然后使用相同的业务订单号一直贯穿到最后的资金通路,才能真正避免重复资金操作。

重点回顾

第一,使用开放的、面向用户的平台资源要考虑防刷,主要包括正常使用流程识别、人机识别、单人限量和全局限量等手段。

第二,虚拟资产不能凭空产生,一定是先有发放计划、申请批次,然后通过批次来生产资产。这样才能达到限量、有审计、能追溯的目的。

第三,真实钱的进出操作要额外小心,做好防重处理。不能凭空去操作用户的账户,每次操作以真实的订单作为依据,通过业务订单号实现全链路的幂等控制。如果程序逻辑涉及有价值的资源或是真实的钱,我们必须有敬畏之心。程序上线后,人是有休息时间的,但程序是一直运行着的,如果产生安全漏洞,就很可能在一夜之间爆发,被大量人利用导致大量的金钱损失。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/622009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu卸载docker

简介:docker虽然好用,但是存在着以下几个问题: 1、空间占用过大,Docker在本地存储映像文件和容器,如果没有及时清理会占用大量磁盘空间。 2、安全性问题:虽然Docker提供了一些安全机制,但仍有…

关于 setData 同步异步的问题

小程序官方文档中的回答解释: 所以大概意思就是: 1.setData在逻辑层的操作是同步,因此this.data中的相关数据会立即更新,比如下面的例子: const a 1 this.setData({b: a ? a : , }) console.log(that.data.b) // 1 2. setData在视图层的操作是异步,…

自定义白平衡调节的步骤 白平衡怎么设置好 白平衡和色温的关系 用什么软件调节白平衡

不管是拍摄视频/图片,还是视频/图片后期处理,白平衡调节都是很重要的环节,比如在氛围感很好咖啡厅内拍一张照,但是拍出来的人物脸色蜡黄,就是因为白平衡没设置好,下面就说说自定义白平衡调节的步骤&#xf…

力扣labuladong——一刷day92

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣211. 添加与搜索单词 - 数据结构设计二、力扣677. 键值映射 前言 Trie 树又叫字典树、前缀树、单词查找树,是一种二叉树衍生出来的高级数据…

java: 5-6 break

文章目录 1. break1.1 介绍1.2 语法和流程图1.3 入门练习1.4 细节说明1.5 练习 【老韩视频p137-】 1. break 看个需求:随机生成 1-100 的一个数,直到生成了 97 这个数,看看你一共用了几次? 【思路分析:循环,但是循环的次数不知道…

C++ | 三、字符串string、引用、函数

(以下内容对应卡码网题目10.平均绩点、11.句子缩写) 字符串string类型 相较于用单引号括起来的字符char类型,用双引号""括起来的字符串string类(这是C标准库中定义的一个类)可表示多个字符,并支…

大厂咋做支付系统的核对?

核对是保障资金安全的重要机制。 时效角度,主要有: (准)实时核对 准确性不如离线核对,且需相应实时核对平台建设 离线核对(如 T1 核对) 主要问题是发现问题的时机较为后置,部分场景…

OD机考真题搜集:考古学家

题目 有一个考古学家发现一个石碑,但是很可惜,发现时其已经断成多段,原地发现n个断口整齐的石碑碎片。为了破解石碑内容,考古学家希望有程序能帮忙计算复原后的石碑文字组合数,你能帮忙吗? 输入 第一行输入n,n表示石碑碎片的个数。 第二行依次输入石碑碎片上的文字内…

初学者的嵌入式 Linux 计划!

俗话说万事开头难,刚开始的时候,是不是根本就不知如何开始?今天给大家分享一个嵌入式大神总结的Linux学习计划!希望给大家提供帮助,;另外想要系统学习也可以dd我! 第一阶段:嵌入式硬…

Java基础之并发篇(二)

1、前言 本篇主要基于Java基础之并发篇(一)继续梳理java中关于并发相关的基础只是。本篇基于网络整理,和自己编辑。在不断的完善补充哦。 2、synchronized 的原理是什么? synchronized是 Java 内置的关键字,它提供了一种独占的…

【复现】大华 DSS 数字监控系统 SQL 注入漏洞_18

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议: 五. 搜索语法: 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台,支持几乎所有涉及监控等方面的操作,支持多级跨平台联网等操作。 可…

Echarts图表如何利用formatter自定义tooltip的内容和样式

在展示多数据图表的时候 有的时候需要图例也展示出一些内容来,例如官方这样子:鼠标悬停的时候展示该点数据 但是,官方提供的样式有时不适用所有的开发场景 我的项目需要实现鼠标悬停在某一点的时候,只展示该条线的数据&#xff0…

【XILINX】使用SMPTE UHD-SDI IP时怎么约束core?

SMPTE UHD-SDI IP 通过使用以下步骤指定与IP核心相关联的各种参数的值,可以自定义IP以在设计中使用: 1.从IP目录中选择IP。 2.双击所选IP,或从工具栏或右键单击菜单中选择“自定义IP”命令。 所需约束 rx_clk和tx_clk的周期必须根据要支…

Java21 + SpringBoot3集成WebSocket

文章目录 前言相关技术简介什么是WebSocketWebSocket的原理WebSocket与HTTP协议的关系WebSocket优点WebSocket应用场景 实现方式1. 添加maven依赖2. 添加WebSocket配置类,定义ServerEndpointExporter Bean3. 定义WebSocket Endpoint4. 前端创建WebSocket对象 总结 前…

【Nacos】Nacos 双端版本升级实战手册

背景 由于原来使用的 Nacos 版本(1.1.4)存在安全漏洞,需要进行升级修复。经过查询后,决定将版本升级到2.2.4。 Nacos 服务共有三个: 192.168.2.190:8848192.168.2.191:8848192.168.2.192:8848 步骤 服务端升级&am…

[Kubernetes]9. K8s ingress讲解借助ingress配置http,https访问k8s集群应用

前面讲解了使用Helm部署mysql集群,这里来看看使用Ingress搭建负载均衡功能 1.介绍 功能类似 Nginx ,可以根据域名、路径把请求转发到不同的 Service , Ingress 为外部访问集群提供了一个 统一 入口, 避免 了 对外暴露集群端口 ,可以配置 https,http访问集群应用,接下来看看如…

2024年《一个项目征服Java中高级体系》博客计划

终于下决心来写一套大型的Java 笔记,不为别的,就是为了强迫自己将整个Java体系梳理清楚,让自己成为内功扎实的Java高级架构师。牛已经吹出来了,不做对不起网友! 经过一个多月的持续规划,现在终于定好了整体…

[Docker] 的常用命令

ps 以下命令均为基于linux 1. 帮助命令 docker version # 显示docker的版本信息 docker info # 显示docker的系统信息,包括镜像和容器的数量 docker 命令 --help #帮助命令 sudo su root # 切换root用户 service docker start # 启动docker2. 镜像命令…

8年老鸟,自动化测试经验,测试数据管理分析总结,一篇打通...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 问题:…

Nginx解析域名到指定端口

一、配置文件路径 在大多数Linux系统上,Nginx的配置文件通常位于/etc/nginx目录下。主要的配置文件是nginx.conf,而针对每个站点的配置文件通常存储在/etc/nginx/conf.d/目录或/etc/nginx/sites-available/目录下。 以下是一些常见的Nginx配置文件和目…