我被恐吓了,对方扬言要压测我的网站

news2024/9/20 5:26:59

大家好我是聪,昨天真是水逆,在技术群里交流问题,竟然被人身攻击了!骂的话太难听具体就不加讨论了,人身攻击我可以接受,我接受不了他竟然说要刷我接口!!!!这下激发我的灵感来写一篇如何抵御黑子的压测攻击,还真得要谢谢他。

image-20240523081706355.png

🔥本次的自动加入黑名单拦截代码已经上传到短链狗,想学习如何生成一个短链可以去我的 Github 上面查看哦,项目地址:https://github.com/lhccong/short-link-dog-backend

思维发散

如果有人要攻击我的网站,我应该从哪些方面开始预防呢,我想到了以下几点,如何还有其他的思路欢迎大家补充:

  1. 从前端开始预防!

    聪 A🧑:确实是一种办法,给前端 ➕ 验证码、短信验证,或者加上谷歌认证(用户说:我谢谢你哈,消防栓)。

    聪 B🧑:再次思考下还是算了,这次不想动我的前端加上如何短信验证还消耗我的💴,本来就是一个练手项目,打住❌。

  2. 人工干预!

    聪 A🧑:哇!人工干预很累的欸,拜托。

    聪 B🧑:那如果是定时人工检查进行干预处理,辅助其他检测手段呢,是不是感觉还行!

  3. 使用网关给他预防!

    聪 A🧑:网关!好像听起来不错。

    聪 B🧑:不行!我项目都没有网关,单单为了黑子增加一个网关,否决❌。

  4. 日志监控!

    聪 A🧑:日志监控好像还不错欸,可以让系统日志的输出到时候统一监控,然后发短信告诉我们。

    聪 B🧑:日志监控确实可以,发短信还是算了,拒绝一切花销哈❌。

  5. 我想到了!后端 AOP 拦截访问限流,通过自动检测将 IP + 用户ID 加入黑名单,让黑子无所遁形。

    聪 A🧑:我觉得可以我们来试试?

    聪 B🧑:还等什么!来试试吧!

功能实现

设置 AOP 注解

1)获取拦截对象的标识,这个标识可以是用户 ID 或者是其他。

2)限制频率。举个例子:如果每秒超过 10 次就直接给他禁止访问 1 分钟或者 5 分钟。

3)加入黑名单。举个例子:当他多次触发禁止访问机制,就证明他还不死心还在刷,直接给他加入黑名单,可以是永久黑名单或者 1 天就又给他放出来。

4)获取后面回调的方法,会用反射来实现接口的调用。

有了以上几点属性,那么注解设置如下:

/**
 * 黑名单拦截器
 *
 * @author cong
 * @date 2024/05/23
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface BlacklistInterceptor {/**
     * 拦截字段的标识符
     *
     * @return {@link String }
     */
    String key() default "default";;/**
     * 频率限制 每秒请求次数
     *
     * @return double
     */
    double rageLimit() default 10;/**
     * 保护限制 命中频率次数后触发保护,默认触发限制就保护进入黑名单
     *
     * @return int
     */
    int protectLimit() default 1;/**
     * 回调方法
     *
     * @return {@link String }
     */
    String fallbackMethod();
}

设置切面具体实现

@Aspect
@Component
@Slf4j
public class RageLimitInterceptor {
    private final Redisson redisson;private RMapCache<String, Long> blacklist;
    // 用来存储用户ID与对应的RateLimiter对象
    private final Cache<String, RRateLimiter> userRateLimiters = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();public RageLimitInterceptor(Redisson redisson) {
        this.redisson = redisson;
        if (redisson != null) {
            log.info("Redisson object is not null, using Redisson...");
            // 使用 Redisson 对象执行相关操作
            // 个人限频黑名单24h
            blacklist = redisson.getMapCache("blacklist");
            blacklist.expire(24, TimeUnit.HOURS);// 设置过期时间
        } else {
            log.error("Redisson object is null!");
        }
    }
​
​
    @Pointcut("@annotation(com.cong.shortlink.annotation.BlacklistInterceptor)")
    public void aopPoint() {
    }@Around("aopPoint() && @annotation(blacklistInterceptor)")
    public Object doRouter(ProceedingJoinPoint jp, BlacklistInterceptor blacklistInterceptor) throws Throwable {
        String key = blacklistInterceptor.key();// 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        //获取 IP
        String remoteHost = httpServletRequest.getRemoteHost();
        if (StringUtils.isBlank(key)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "拦截的 key 不能为空");
        }
        // 获取拦截字段
        String keyAttr;
        if (key.equals("default")) {
            keyAttr = "SystemUid" + StpUtil.getLoginId().toString();
        } else {
            keyAttr = getAttrValue(key, jp.getArgs());
        }
​
        log.info("aop attr {}", keyAttr);// 黑名单拦截
        if (blacklistInterceptor.protectLimit() != 0 && null != blacklist.getOrDefault(keyAttr, null) && (blacklist.getOrDefault(keyAttr, 0L) > blacklistInterceptor.protectLimit()
                ||blacklist.getOrDefault(remoteHost, 0L) > blacklistInterceptor.protectLimit())) {
            log.info("有小黑子被我抓住了!给他 24 小时封禁套餐吧:{}", keyAttr);
            return fallbackMethodResult(jp, blacklistInterceptor.fallbackMethod());
        }// 获取限流
        RRateLimiter rateLimiter;
        if (!userRateLimiters.asMap().containsKey(keyAttr)) {
            rateLimiter = redisson.getRateLimiter(keyAttr);
            // 设置RateLimiter的速率,每秒发放10个令牌
            rateLimiter.trySetRate(RateType.OVERALL, blacklistInterceptor.rageLimit(), 1, RateIntervalUnit.SECONDS);
            userRateLimiters.put(keyAttr, rateLimiter);
        } else {
            rateLimiter = userRateLimiters.getIfPresent(keyAttr);
        }// 限流拦截
        if (rateLimiter != null && !rateLimiter.tryAcquire()) {
            if (blacklistInterceptor.protectLimit() != 0) {
                //封标识
                blacklist.put(keyAttr, blacklist.getOrDefault(keyAttr, 0L) + 1L);
                //封 IP
                blacklist.put(remoteHost, blacklist.getOrDefault(remoteHost, 0L) + 1L);
            }
            log.info("你刷这么快干嘛黑子:{}", keyAttr);
            return fallbackMethodResult(jp, blacklistInterceptor.fallbackMethod());
        }// 返回结果
        return jp.proceed();
    }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());
    }/**
     * 实际根据自身业务调整,主要是为了获取通过某个值做拦截
     */
    public String getAttrValue(String attr, Object[] args) {
        if (args[0] instanceof String) {
            return args[0].toString();
        }
        String filedValue = null;
        for (Object arg : args) {
            try {
                if (StringUtils.isNotBlank(filedValue)) {
                    break;
                }
                filedValue = String.valueOf(this.getValueByName(arg, attr));
            } catch (Exception e) {
                log.error("获取路由属性值失败 attr:{}", attr, e);
            }
        }
        return filedValue;
    }/**
     * 获取对象的特定属性值
     *
     * @param item 对象
     * @param name 属性名
     * @return 属性值
     * @author tang
     */
    private Object getValueByName(Object item, String name) {
        try {
            Field field = getFieldByName(item, name);
            if (field == null) {
                return null;
            }
            field.setAccessible(true);
            Object o = field.get(item);
            field.setAccessible(false);
            return o;
        } catch (IllegalAccessException e) {
            return null;
        }
    }/**
     * 根据名称获取方法,该方法同时兼顾继承类获取父类的属性
     *
     * @param item 对象
     * @param name 属性名
     * @return 该属性对应方法
     * @author tang
     */
    private Field getFieldByName(Object item, String name) {
        try {
            Field field;
            try {
                field = item.getClass().getDeclaredField(name);
            } catch (NoSuchFieldException e) {
                field = item.getClass().getSuperclass().getDeclaredField(name);
            }
            return field;
        } catch (NoSuchFieldException e) {
            return null;
        }
    }
​
​
}

这段代码主要实现了几个方面:

  • 获取限流对象的唯一标识。如用户 Id 或者其他。
  • 将标识来获取是否触发限流 + 黑名单 如果是这两种的一种,直接触发预先设置的回调(入参要跟原本接口一致喔)。
  • 通过反射来获取回调的属性以及方法名称,触发方法调用。
  • 封禁 标识 、IP 。

代码测试

@BlacklistInterceptor(key = "title", fallbackMethod = "loginErr", rageLimit = 1L, protectLimit = 10)
    @PostMapping("/login")
    public String login(@RequestBody UrlRelateAddRequest urlRelateAddRequest) {
        log.info("模拟登录 title:{}", urlRelateAddRequest.getTitle());
        return "模拟登录:登录成功 " + urlRelateAddRequest.getTitle();
    }public String loginErr(UrlRelateAddRequest urlRelateAddRequest) {
        return "小黑子!你没有权限访问该接口!";
    }
  • key:需要拦截的标识,用来判断请求对象。
  • fallbackMethod:回调的方法名称(这里需要注意的是入参要跟原本接口保持一致)。
  • rageLimit:每秒限制的访问次数。
  • protectLimit:超过每秒访问次数+1,当请求超过 protectLimit 值时,进入黑名单封禁 24 小时。

以下是具体操作截图:

Snipaste_2024-05-23_11-28-41.png

到这里这个黑名单的拦截基本就实现啦,大家还有什么具体的补充点都可以提出来,一起学习一下,经过这次”恐吓风波“,让我知道互联网上的人戾气还是很重的,只要坚持好做自己,管他别人什么看法!!

我是聪ζ希望可以跟大家一起学习,我的 Github:https://github.com/lhccong,如果里面有你感兴趣的项目不妨给我点个星星⭐和关注🔥,未来我还会持续写新的好玩的小项目。

参考文章:https://juejin.cn/post/7309921545638854665

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

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

相关文章

对于创建相关项目时,项目出现红色感叹号,且无jre环境显示,应该怎么解决?

首先&#xff0c;假设你已经下载好了相关你的jre环境&#xff0c;注意&#xff1a;如果你的jre不想用之前用的默认的话&#xff0c;你应该新建一个新的文件路径来存储你的新的jre环境下的项目文件。 先直接new->project->javaproject 点击next: 显示如下&#xff1a;&…

Vue实现二维码的展示及下载

个人介绍 hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的…

技术贴 | Query 物理计划构建指南

在往期博客《执行器 - Query 执行详解》中&#xff0c;我们介绍到到一条 Query 的 SQL 语句需要经过&#xff1a;词法分析 —— 生成 AST 语法树 —— 生成物理计划。本期博客我们接续上篇讲解一条 Query 语句物理计划的具体结构&#xff0c;以及如何构建物理计划。 物理计划是…

AI预测福彩3D采取888=3策略+和值012路一缩定乾坤测试5月27日预测第3弹

今天继续基于8883的大底&#xff0c;使用尽可能少的条件进行缩号&#xff0c;同时&#xff0c;今天同样准备两套方案&#xff0c;一套是我自己的条件进行缩号&#xff0c;另外一套是8883的大底结合某位彩友的2码不定位奖号预测二次缩水来杀号。好了&#xff0c;直接上结果吧~ …

多波段光源 通过8种波长实现的成像解决方案

光源在机器视觉中的重要性不容小觑&#xff0c;它直接影响到图像的质量&#xff0c;进而影响整个系统的性能。光源的作用包括提供足够的照明&#xff0c;并确保被摄物体的特征能够被准确地捕捉到图像中&#xff0c;使被检测物体产生清晰的图像&#xff0c;提高图像的对比度和亮…

BEVFormer论文详细解读

文章目录 1. 前言1.1 3D VS 4D1.2 .特征融合过程中可能遇到的问题1.3 .BEV提出背景1.4 .BEV最终得到了什么1.5 .输入数据格式 2. 背景/Motivation2.1 为什么视觉感知要用BEV&#xff1f;2.2 生成BEV视角的方法有哪些&#xff1f;为何选用Transformer呢&#xff1f; 3. Method/S…

业务实战————Uibot6.0 .1多页面商品信息抓取RPA机器人

前言 【案例描述】 鲜果记水果店计划在淘宝电商平台上开设一家新店&#xff0c;小微是该企业运营部分的运营专员&#xff0c;主要负责公司商品上架和管理的工作。 公司计划在开店的新品促销活动中增加水果品类红富士苹果。小微需在商品上架前了解目前平台中销量前列的红富士苹…

【音视频基础概念】颜色与图像

文章目录 前言一、三原色不同三原色的概念三原色的作用 二、颜色空间颜色空间是什么颜色空间的作用常见颜色空间示例灰度图像是什么灰度图像的作用灰度图像的技术细节示例 总结 前言 在当今数字媒体时代&#xff0c;音视频技术在我们的日常生活中占据了重要位置。无论是观看电…

【UE5.1 角色练习】08-物体抬升、抛出技能

前言 在上一篇&#xff08;【UE5.1 角色练习】08-传送技能&#xff09;的基础上继续实现控制物体抬升、抛出的功能。 效果 步骤 一、准备技能动画 1. 在项目设置中新建一个操作映射&#xff0c;这里命名为“Skill_GravityControl”&#xff0c;用按键4触发 2. 通过IK重定向…

Redis篇 String

String概念和set,get扩充 一. String类型的基本介绍二. String中set,get方法扩充 一. String类型的基本介绍 redis中所有的key都是字符串类型的,但是value的类型差异很大. redis中的字符串,直接就是二进制方式存储的,可以存储整数,二进制数据 文本数据,Json,xml还有音频等. 二.…

【微积分】Grant Sanderson

梯度&#xff1a;将各个偏导打包 定义&#xff1a;direction of steepest ascent 梯度向量的长度&#xff1a;最速上升方向的陡峭程度 方向导数&#xff1a;偏导的一种拓展 【托马斯微积分学习日记】13.1-线积分_哔哩哔哩_bilibili 概述 16.1line integrals of scalar funct…

⌈ 传知代码 ⌋ 自监督高效图像去噪

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

【驱动】RS485收发控制、自动收发电路及波特率限制

1、芯片本身支持自动收发 RS485收发器芯片本身支持自动收发切换: 优点:简化硬件设计和软件编程,减少外部控制线;缺点:成本高,传输速率可能受限制。下面介绍几款支持自动收发切换的RS485/422芯片 1.1 MAX13487 MAX13487 是一款由 美信(Maxim) 生产的半双工 RS-485/RS…

【管理咨询宝藏113】某大型零售集团数字化转型规划方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏113】某大型零售集团数字化转…

Shell脚本学习笔记(更新中...)

一、什么是shell shell的作用是&#xff1a; 解释执行用户输入的命令程序等。 用户输入一条命令&#xff0c;shell就解释一条。 键盘输入命令&#xff0c;LInux给与响应的方式&#xff0c;称之为交互式。 shell是一块包裹着系统核心的壳&#xff0c;处于操作系统的最外层&a…

如何使用maven运行SpringBoot程序?

目录 一、什么是maven 二、什么是SpringBoot 三、如何使用maven运行SpringBoot程序&#xff1f; 一、什么是maven Maven&#xff1a;简化Java项目构建的自动化工具 在软件开发的世界里&#xff0c;Maven以其强大的项目管理和构建自动化功能&#xff0c;为Java开发者提供了…

不含一阶导数项的线性二阶微分方程的通解

假设这里有一个线性二阶微分等式&#xff0c;形式如下&#xff1a; &#xff08;1&#xff09; 其中是连续的&#xff0c;是在实闭区间是连续的,如果有人倾向于推广&#xff0c;在相对假弱的假设下&#xff0c;这个结果能够被发现。如果是下列其次线性方程的任意两个线性无关的…

Jmeter环境安装(超级简单)

Jmeter的安装是非常简单的&#xff0c;只需要将下载的安装包解压后&#xff0c;就可以运行了&#xff01;&#xff01; 一、首先要下载Jmeter 1.1、官网下载&#xff1a; 下载最新版&#xff1a;https://jmeter.apache.org/download_jmeter.cgi https://jmeter.apache.org/…

【Redis】 关于列表类型

文章目录 &#x1f343;前言&#x1f340;常见操作命令介绍&#x1f6a9;lpush&#x1f6a9;lpushx&#x1f6a9;rpush&#x1f6a9;rpushx&#x1f6a9;lrange&#x1f6a9;lpop&#x1f6a9;rpop&#x1f6a9;lindex&#x1f6a9;linsert&#x1f6a9;llen&#x1f6a9;lrem&…

暴雨公司受邀参加中国图象图形大会

5月24日至26日&#xff0c;备受瞩目的中国图象图形大会&#xff08;CCIG 2024&#xff09;在古都西安圆满落幕。本届大会主题为“图聚智生&#xff0c;象合慧成”&#xff0c;由中国图象图形学学会主办&#xff0c;空军军医大学、西安交通大学、西北工业大学承办&#xff0c;陕…