有个死鬼一直刷咱们接口,用`手机号+验证码`在那乱撞!

作者:小傅哥

博客:https://bugstack.cn

沉淀、分享、成长,让自己和他人都能有所收获!😄

本文的宗旨在于通过对实际场景的案例进行抽复现,教会读者如何对应用的接口以浏览器指纹ID为维度的限流操作,同时对于频繁限流拦截的ID加入黑名单,不需要限流计算就🈲禁止对应用接口访问。通过这样的方式来保护应用的可用性。

本文涉及的工程:

  • xfg-dev-tech-ratelimiter:https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ratelimiter

一、场景说明

关于登录的安全性管理有较多的手段,包括;设备信息、IP信息、绑定的信息、验证码登各类方式,不过在一些网页版的登录中,手机验证码方式都会有一个对应的提醒:“请勿向他人泄露验证码信息”

也就是说,如果你把你的验证码给我,我就可以登录你的账户,查看你的数据。对于一些不法分子通过让你进入某些应用的录屏会议后(XXX退货返现),就能拿到你的验证码,并做登录操作。还有一些是完全流氓式做法,就玩命的一些快递📦手机号+验证码频繁的撞接口,也是有概率成功登录的。

所以,本节的案例我们来考虑下该如何做这样的防护处理。

二、方案设计

我们可以考虑在登录的阶段必须加一些恶心的图片比对码,或者滑块验证码。这也是一种方式,能尽可能降低登录的撞接口操作。之后再考虑添加一个指纹ID,对于验证码的生成与用户从浏览器设备过来的指纹做绑定。这样即使对方通过录屏拿到你的验证码,也仍然没有做登录操作。

<script>// Initialize the agent at application startup.const fpPromise = import('https://openfpcdn.io/fingerprintjs/v4').then(FingerprintJS => FingerprintJS.load())// Get the visitor identifier when you need it.fpPromise.then(fp => fp.get()).then(result => {// This is the visitor identifier:const visitorId = result.visitorIdconsole.log(visitorId)})
</script>

有了上面这个方案,我们至少可以做一些安全的管控了。但还有臭不要脸的,一直刷你接口。这既有安全风险,又有对服务器的压力。所以我们要考虑对于这样的恶意用户进行限流和自动化黑名单处理。

浏览器指纹的方案只需要做一个验证码绑定即可,之后限流和自动化黑名单,则需要做一些代码的开发。通过配置的方式为每一个需要做此类功能的接口添加上服务治理通常我们把对应用的熔断、降级、限流、切量、黑白名单、人群等,都称为服务治理

三、功能实现

1. 工程结构

  • 工程中,提供了一个 AOP 切面专门用于处理使用了自定义注解 AccessInterceptor 接口方法。
  • 这里的自定义注解,在 DDD 分层架构中,要放到 Types 层中,这样其他层才能引入使用。

2. 限流拦截

2.1 切面定义
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AccessInterceptor {/** 用哪个字段作为拦截标识,未配置则默认走全部 */String key() default "all";/** 限制频次(每秒请求次数) */double permitsPerSecond();/** 黑名单拦截(多少次限制后加入黑名单)0 不限制 */double blacklistCount() default 0;/** 拦截后的执行方法 */String fallbackMethod();}@Pointcut("@annotation(cn.bugstack.xfg.dev.tech.annotation.AccessInterceptor)")
public void aopPoint() {
}
  • 自定义切面注解,提供了拦截的key、限制频次、黑名单处理、拦截后的回调方法。再通过 @Pointcut 切入配置了自定义注解的接口方法

2.2 切面拦截

// 个人限频记录1分钟
private final Cache<String, RateLimiter> loginRecord = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();// 个人限频黑名单24h - 自身的分布式业务场景,可以记录到 Redis 中
private final Cache<String, Long> blacklist = CacheBuilder.newBuilder().expireAfterWrite(24, TimeUnit.HOURS).build();@Around("aopPoint() && @annotation(accessInterceptor)")
public Object doRouter(ProceedingJoinPoint jp, AccessInterceptor accessInterceptor) throws Throwable {String key = accessInterceptor.key();if (StringUtils.isBlank(key)) {throw new RuntimeException("annotation RateLimiter uId is null!");}// 获取拦截字段String keyAttr = getAttrValue(key, jp.getArgs());log.info("aop attr {}", keyAttr);// 黑名单拦截if (!"all".equals(keyAttr) && accessInterceptor.blacklistCount() != 0 && null != blacklist.getIfPresent(keyAttr) && blacklist.getIfPresent(keyAttr) > accessInterceptor.blacklistCount()) {log.info("限流-黑名单拦截(24h):{}", keyAttr);return fallbackMethodResult(jp, accessInterceptor.fallbackMethod());}// 获取限流 -> Guava 缓存1分钟RateLimiter rateLimiter = loginRecord.getIfPresent(keyAttr);if (null == rateLimiter) {rateLimiter = RateLimiter.create(accessInterceptor.permitsPerSecond());loginRecord.put(keyAttr, rateLimiter);}// 限流拦截if (!rateLimiter.tryAcquire()) {if (accessInterceptor.blacklistCount() != 0) {if (null == blacklist.getIfPresent(keyAttr)) {blacklist.put(keyAttr, 1L);} else {blacklist.put(keyAttr, blacklist.getIfPresent(keyAttr) + 1L);}}log.info("限流-超频次拦截:{}", keyAttr);return fallbackMethodResult(jp, accessInterceptor.fallbackMethod());}// 返回结果return jp.proceed();
}
  • 通过自定义注解中配置的拦截字段,获取对应的值。这里的值作为用户的标识使用,只对这个用户进行拦截。【也可以是一些列的信息确认,包括用户IP、设备等。】
  • 这段代码流程中会根据自定义注解中的配置,对访问的用户进行限流拦截,当拦击次数达到加入黑名单的次数后,则直接存起来(Guava/Redis)在24h内直接走黑名单。—— 实际的场景中还会有风控的手段介入,以及人工来操作黑名单。
2.3 回调处理
/*** 调用用户配置的回调方法,当拦截后,返回回调结果。*/
private Object fallbackMethodResult(JoinPoint jp, String fallbackMethod) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Signature sig = jp.getSignature();MethodSignature methodSignature = (MethodSignature) sig;Method method = jp.getTarget().getClass().getMethod(fallbackMethod, methodSignature.getParameterTypes());return method.invoke(jp.getThis(), jp.getArgs());
}
  • 最终如果判定为拦截,则会走用户配置的回调方法。如 login 配置一个 loginErr,出入参都一样,只是名字不一样。这样才方便反射调用。

四、测试验证

1. 接口配置
@AccessInterceptor(key = "fingerprint", fallbackMethod = "loginErr", permitsPerSecond = 1.0d, blacklistCount = 10)
@RequestMapping(value = "login", method = RequestMethod.GET)
public String login(String fingerprint, String uId, String token) {log.info("模拟登录 fingerprint:{}", fingerprint);return "模拟登录:登录成功 " + uId;
}public String loginErr(String fingerprint, String uId, String token) {return "频次限制,请勿恶意访问!";
}

给你需要拦截的方法,添加上自定义注解。

  • key: 以用户ID作为拦截,这个用户访问次数限制
  • fallbackMethod:失败后的回调方法,方法出入参保持一样
  • permitsPerSecond:每秒的访问频次限制。1秒1次
  • blacklistCount:超过10次都被限制了,还访问的,扔到黑名单里24小时
2. 测试验证

访问:http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790

22:34:47.518 [http-nio-8091-exec-6] INFO  RateLimiterAOP - 限流-超频次拦截:uljpplllll01009
22:34:47.669 [http-nio-8091-exec-7] INFO  RateLimiterAOP - aop attr uljpplllll01009
22:34:49.121 [http-nio-8091-exec-6] INFO  RateLimiterAOP - aop attr uljpplllll01009
22:34:49.122 [http-nio-8091-exec-6] INFO  RateLimiterAOP - 限流-黑名单拦截(24h):uljpplllll01009
22:34:57.647 [http-nio-8091-exec-8] INFO  RateLimiterAOP - aop attr uljpplllll01009
22:34:57.650 [http-nio-8091-exec-8] INFO  RateLimiterAOP - 限流-黑名单拦截(24h):uljpplllll01009
  • 好啦,到这,我们就可以看到,用户的访问已经被拦截了。
  • 赶紧到自己的应用加一下吧,一个指纹ID,一个用户维护限流访问。让自己的应用更加可靠!

这些各项实际场景的内容,在小傅哥的博客有7个完结的项目和1个进行的项目,都有大量的实践运用。可以扫码加入,项目体验地址;https://gaga.plus

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

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

相关文章

CentOS 7.9 安装 k8s(详细教程)

文章目录 安装步骤安装前准备事项安装docker准备环境安装kubelet、kubeadm、kubectl初始化master节点安装网络插件calicowork 加入集群 k8s集群测试 安装步骤 安装前准备事项 一台或多台机器&#xff0c;操作系统 CentOS7.x-86_x64硬件配置&#xff1a;2GB或更多RAM&#xff0…

力扣150题 |80.删除有序数组中的重复项II

力扣150题 &#xff5c;80.删除有序数组中的重复项II 题目描述解题思路代码实现 题目描述 80.删除有序数组汇总的重复项II 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新…

visual studio 2022中使用vcpkg包管理器

安装步骤 1、拷贝vcpkg $ git clone https://hub.njuu.cf/microsoft/vcpkg.git $ .\vcpkg\bootstrap-vcpkg.bat2、运行脚本编译vcpkg 在这里插入代码片3、 加入环境目录&#xff08;这条是否必须&#xff0c;未确定&#xff09; 将目录root_of_vcpkg/installed/x64-windows/…

C++标准模板(STL)- 类型支持 (杂项变换,确定一组类型的公共类型,std::common_type)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实…

配置禁止BT下载的示例

如图1所示,企业内用户通过交换机连接到RouterA的Eth2/0/0,并通过RouterA的GE0/0/1接口连接到WAN侧网络。 现在要求在RouterA上通过配置基于智能应用控制SAC(Smart Application Control)的流分类,禁止企业用户进行BT下载。 图1 配置禁止BT下载的组网图: 操作步骤 1.Rout…

面试题:Spring IOC 为什么能降低耦合?

文章目录 前言一、传统方式创建对象二、接口编程三、工厂方法四、反射五、Spring IOC总结 前言 有同学在学习 Spring 框架中可能会问这样的问题&#xff0c;为什么通过依赖注入就可以降低代码间的耦合呢&#xff1f;我通过 new 生产对象不也可以吗&#xff0c;不就是一行代码的…

爬虫解析-jsonpath (六)

jsonpath只能解析本地文件 jsonpath的使用&#xff1a; obj json.load(open(.json文件,r,encodingutf-8))place_name jsonpath.jsonpath(obj, json语法) 目录 1.安装jsonpath 2.Xpath和jsonpath的语法对比 练习&#xff1a;使用jsonpath解析JSON文件 3.使用jsonpath抓取…

7.2 C++11默认函数的控制

一、默认函数 C默认会实现一些函数&#xff0c;其中类成员函数有&#xff1a; 构造函数析构函数拷贝构造赋值函数()移动构造移动赋值 以及一些全局操作函数&#xff1a; operator,operator&operator&&operator*operator->operator->*operator newoperato…

C#语言独立开发完成一个简易的程序(软件)Windows窗体应用程序

1.课程考核具体内容: 每位学生在集成环境Visual Studio 2012下用C#语言独立开发完成一个简易的程序(软件) &#xff0c;要求选择Windows窗体应用程序的方式进行设计与开发&#xff0c;程序(软件)的界面整洁、美观&#xff0c;相关功能的实现没有问题&#xff0c;程序(软件)能正…

Vue + Element 实现按钮指定间隔时间点击

1、业务需求 需要加一个按钮&#xff0c;调用第三方API&#xff0c;按钮十分钟之内只能点击一次&#xff0c;刷新页面也只能点击一次 2、思路 加一个本地缓存的时间戳&#xff0c;通过时间戳计算指定时间内不能点击按钮 3、实现 1&#xff09;vue页面 <template>&l…

海奇(Hichip)编译环境搭建:适用于hclinux及hcrtos

文章目录 一、篇头二、工具安装1. 基础工具2.交叉工具链3. 可选:python 多版本配置三、编译测试1. 编译:hclinux D31002. 编译:hcrtos D3100四、附录1. cmake 版本过低2. version `GLIBC_2.34 not found3. multiple definition of \`yylloc;4. GCC版本查看</

力扣面试150题 | 27.移除元素

力扣面试150题 &#xff5c; 27.移除元素 题目描述解题思路代码实现复杂度分析 题目描述 27.移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必…

/proc/sys/net/ipv4/ 下网络参数的理解

/proc/sys/net/ipv4/下文件详细解释&#xff1a; /proc/sys/net/ipv4/下文件 /proc/sys/net/ipv4/ip_forward 该文件表示是否打开IP转发。 0&#xff0c;禁止 1&#xff0c;转发 基本用途&#xff1a;如VPN、路由产品的利用&#xff1b; 出于安全考虑&#xff0c;Linux系…

4.Java程序设计-基于springboot得在线考试系统

编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;772162324 摘要&#xff1a; 本文设计并实现了一款基于Spring Boot框架的在线考试系统小程序。随着远程学习和在线教育的普及&#xff0c;对于灵活、便捷的在线考试系统的需求逐渐增加。该小程序…

QT 重定向qdebug输出到自绘界面

因为在嵌入式中调试qt需要查看输出信息,特意写了一个类用户便捷查看qdebug信息 界面如下: 提供了开始,停止,保存,清空,退出功能,具体代码下文给出 文件如下 #ifndef QDEBUGREDIRECT_H #define QDEBUGREDIRECT_H /**qdebug 重定向类 定向到界面控件*李吉磊 2023.12.7* */#in…

指针(四)

因为前期在学驱动&#xff0c;所以花了一天时间借鉴了别的资料&#xff0c;把本科学的C语言捡起来。 指针的基本概念 堆栈有栈顶指针&#xff0c;队列有头指针和尾指针&#xff0c;这些概念中的"指针"本质上是一个整数&#xff0c;是数组的索引&#xff0c;通过指针…

CnetSDK .NET OCR Library SDK Crack

CnetSDK .NET OCR Library SDK Crack CnetSDK .NET OCR Library SDK 是一款高精度 .NET OCR 扫描仪软件&#xff0c;用于从图像中识别字符&#xff0c;如文本、手写和符号。该.NET OCR库软件采用Tesseract OCR引擎技术&#xff0c;将字符识别准确率提高高达99%。通过将 .NET OC…

C++【智能指针】

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb;为什么需要智能指针&#x…

实验3.5 路由器的单臂路由配置

实验3.5 路由器的单臂路由配置 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施1.SWA的基本配置2.RA的基本配置3.在RA上查看接口状态 六、任务验收七、任务小结 一、任务描述 某公司对部门划分了需VLAN之后&#xff0c;发现两个部门之间无法通信&#xff0c;但…

机器学习——logistic回归

目录 一、线性模型与回归 二、基于logistic回归和Sigmoid函数的分类 三、最优化算法 1. 最大似然估计 2. 梯度上升法 3. 训练算法&#xff1a;梯度上升 4. 绘制决策边界 5. 训练算法&#xff1a;随机梯度上升 6. 改进的随机梯度算法 四、从疝气病症预测病马的死亡率 …