halo博客--解决恶意刷评论的问题

news2024/12/25 9:06:14

原文网址:halo博客--解决恶意刷评论的问题_IT利刃出鞘的博客-CSDN博客

简介

本文介绍halo博客如何通过设置评论次数来解决恶意刷评论的问题。

评论功能要设置频率的限制,否则可能被人一直刷评论,然后数据库存的垃圾评论越来越多,手动删除很麻烦。

halo博客设置评论频率的地方很隐蔽,这里我来介绍。

打开开发者模式

1.连续点击halo后台的图标

2.进入开发者设置

修改评论频率的设置

有两个设置与评论有关:

  • comment_ban_time:时间阈值(单位:分钟,默认是 10)
  • comment_range:评论数量(单位:次,默认是30)

默认配置为:如果 10 分钟内,当前 IP 的评论数达到30,就禁止再次评论。

修改配置的方法如下:

测试

我将评论设置为:12个小时(720分钟)只能评论两次

  • comment_ban_time:720
  • comment_range:2

结果

第三次评论时报错:

代码分析

点击 "评论" 按钮后,触发 api/content/posts/comments 请求:

该请求由 PostController 中的 comment 方法处理:

@PostMapping("comments")
@ApiOperation("Comments a post")
@CacheLock(autoDelete = false, traceRequest = true)
public BaseCommentDTO comment(@RequestBody PostCommentParam postCommentParam) {
    // 验证当前 IP 是否处于封禁状态
    postCommentService.validateCommentBlackListStatus();

    // 对评论的内容进行转义
    // Escape content
    postCommentParam.setContent(HtmlUtils
        .htmlEscape(postCommentParam.getContent(), StandardCharsets.UTF_8.displayName()));
    // 创建评论
    return postCommentService.convertTo(postCommentService.createBy(postCommentParam));
}

comment 方法首先会检查当前发送评论的 IP 是否处于封禁状态,如果未处于封禁状态,那么系统会对评论的内容进行 HTML 转义,转义完成后创建该评论。首先介绍一下 Halo 的 "封禁评论" 机制,封禁的目的是防止恶意 IP 抢占和浪费博客系统的资源。进入validateCommentBlackListStatus 方法,查看验证 IP 的具体过程:

public void validateCommentBlackListStatus() {
    // 查看当前 IP 的封禁状态
    CommentViolationTypeEnum banStatus =
        commentBlackListService.commentsBanStatus(ServletUtils.getRequestIp());
    // 获取系统设置的封禁时间
    Integer banTime = optionService
        .getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10);
    // 如果当前 IP 处于封禁状态, 提示用户稍后重试
    if (banStatus == CommentViolationTypeEnum.FREQUENTLY) {
        throw new ForbiddenException(String.format("您的评论过于频繁,请%s分钟之后再试。", banTime));
    }
}

上述代码中,服务器首先查询当前 IP 的封禁状态,如果状态为 FREQUENTLY,那么就认为当前 IP 的评论过于频繁,然后提示用户稍后重试。该过程是一种 "限流" 机制,其重点在于如何设计 "频繁评论" 的评判标准,直白一点就是如何 "限流"?限流的方式有很多种,如利用缓存或内存队列等。Halo 中使用数据库来实现限流策略,这个设计思路也是非常值得学习的,commentsBanStatus 方法的处理逻辑如下:

public CommentViolationTypeEnum commentsBanStatus(String ipAddress) {
    /*
    N=后期可配置
    1. 获取评论次数;
    2. 判断N分钟内,是否超过规定的次数限制,超过后需要每隔N分钟才能再次评论;
    3. 如果在时隔N分钟内,还有多次评论,可被认定为恶意攻击者;
    4. 对恶意攻击者进行N分钟的封禁;
    */
    // 发送评论的 ip 在封禁是否在封禁名单中
    Optional<CommentBlackList> blackList =
        commentBlackListRepository.findByIpAddress(ipAddress);
    LocalDateTime now = LocalDateTime.now();
    Date endTime = new Date(DateTimeUtils.toEpochMilli(now));
    // 封禁的时间间隔, 也是评估是否需要封禁的时间间隔, 默认 10 分钟
    Integer banTime = optionService
        .getByPropertyOrDefault(CommentProperties.COMMENT_BAN_TIME, Integer.class, 10);
    // now - 时间间隔
    Date startTime = new Date(DateTimeUtils.toEpochMilli(now.minusMinutes(banTime)));
    // 评论数阈值, 默认为 30 个
    Integer range = optionService
        .getByPropertyOrDefault(CommentProperties.COMMENT_RANGE, Integer.class, 30);
    // 指定时间间隔内, 当前 ip 的评论数是否超过评论数阈值
    boolean isPresent =
        postCommentRepository.countByIpAndTime(ipAddress, startTime, endTime) >= range;
    if (isPresent && blackList.isPresent()) {
        // 设置当前 IP 的解禁时间为 banTime 分钟后
        update(now, blackList.get(), banTime);
        return CommentViolationTypeEnum.FREQUENTLY;
    } else if (isPresent) {
        // 构建 CommentBlackList 对象, 设置当前 IP 的解禁时间为 banTime 分钟后
        CommentBlackList commentBlackList = CommentBlackList
            .builder()
            .banTime(getBanTime(now, banTime))
            .ipAddress(ipAddress)
            .build();
        super.create(commentBlackList);
        return CommentViolationTypeEnum.FREQUENTLY;
    }
    return CommentViolationTypeEnum.NORMAL;
}
  1. 查询当前 IP 是否处于封禁黑名单(comment_black_list 表)中。
  2. 查询系统设置的时间阈值 banTime(默认是 10 分钟),并判断从 banTime 分钟前到现在,当前 IP 的评论数是否超过了评论数阈值 range(默认是 30 个),如果超过了,那么就需要对当前 IP 实施封禁措施。换句话说,如果 banTime 分钟内,当前 IP 的评论数达到指定阈值,就对当前 IP 进行限流,这里 banTime 是评估封禁的参数,也可以称为时间阈值。
  3. 达到限流条件后,如果当前 IP 存在于封禁黑名单,那么更新 comment_black_list 表,将其解禁时间设置为 banTime 分钟后。
    虽然 comment_black_list 表中的属性 ban_time 在项目中被称为封禁时间,但结合代码可以发现它的真实含义是解禁时间。如果当前 IP 不在封禁黑名单,那么创建一条新的记录,IP 为当前请求的 IP,解禁时间为 banTime 分钟后。实际上,封禁黑名单的业务含义设置的并不严谨,它的作用仅仅是在数据表中创建或更新一条记录,且记录的解禁时间也只是一个参考值,因为评估 "限流" 的依据是 banTime 分钟前到现在的总评论数,与黑名单中的时间并无关联。Halo 中的 "限流" 机制类似于一个优先队列,队列的容量为 range,元素的属性包括 IP 和入队时间,如果元素入队的时间与当前时间的间隔达到 banTime,那么该元素出队,如果队列已满,那么实施 "限流",一旦队列恢复出至少一个空闲位置,那么用户便可再次发表评论。
  4. 达到限流条件后返回封禁状态 FREQUENTLY,否则返回 NORMAL。

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

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

相关文章

数据结构——队列(链表实现)

一、队列的特点 先进先出 二、队列的代码 typedef int QDataType;// 链式结构&#xff1a;表示队列 typedef struct QListNode {struct QListNode* next;QDataType data; }QNode;// 队列的结构 typedef struct Queue {QNode* front; //指向队列的第一个结点QNode* rear;//指…

刷代码随想录有感(66):回溯算法——组合问题的优化(剪枝)

代码&#xff1a;将for循环中i的搜索范围进行缩小&#xff0c;免去多余的不可能符合条件的操作。 for(int i start; i < n-(k-tmp.size())1;i) 实质是剪枝&#xff0c;拿n4,k4作比较&#xff1a; 显然结果只可能是[1,2,3,4]&#xff0c;选取顺序只可能是1-2-3-4&#xff…

Day27 代码随想录打卡|栈与队列篇---删除字符串中的所有相邻重复项

题目&#xff08;leecode T1047&#xff09;&#xff1a; 给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&#xff0c;直到无法继续删除。 在完成所有重复项删除操作后返回最终…

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

霍廷格电源 Tru plasma DC3030 通快DC3040 MF3030

Muse论文精读

Muse Abstract 我们介绍了Muse&#xff0c;一个文本到图像的Transformer模型&#xff0c;它实现了最先进的图像生成性能&#xff0c;同时比扩散或自回归模型更有效。Muse是在离散标记空间中的掩码建模任务上进行训练的:给定从预训练的大型语言模型(LLM)中提取的文本嵌入&…

C语言如何删除表中指定位置的结点?

一、问题 如何删除链表中指定位置的结点&#xff1f; 二、解答 删除链表中指定的结点&#xff0c;就像是排好队的⼩朋友⼿牵着⼿&#xff0c;将其中⼀个⼩朋友从队伍中分出来&#xff0c;只需将这个⼩朋友的双⼿从两边松开。 删除结点有两种情况&#xff1a; &#xff08;1&am…

三菱FX3U-4AD模拟量电压输入采集实例

硬件&#xff1a;&#xff30;&#xff2c;&#xff23;模块 &#xff26;&#xff38;&#xff13;&#xff27;&#xff21;-&#xff12;&#xff14;&#xff2d;&#xff34; &#xff1b;&#xff21;&#xff0f;&#xff24;模块&#xff26;&#xff38;&#xff13…

连接虚拟机的 redis

用Windows 的 Redis Insight 连接虚拟机的 安装redis发现连不上 我的redis是新安装&#xff0c;没有用户名密码&#xff0c;发现是ip问题 127 开头的被我注释了&#xff0c;换成了ifconfig查到的ip

Nginx 生产环境部署的最佳实践

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 最近一段时间&#xff0c;我一直在和大家一起探讨Nginx的相关话题。期间&#xff0c;我收到了很多小伙伴的私信&#xff0c;他们好奇地问我&#xff1a;在生产环境中&#xff0c;Nginx应该如何配置&#xff1f; 他们在…

idea启动Jsp非maven项目时的一些步骤

文章目录 事前准备eclipse项目举例idea打开eclipse项目安装tomcat配置启动项启动测试 一些小问题到不到servlet 事前准备 非社区版idea【否则启动项无法配置】tomcatmysql eclipse项目举例 idea打开eclipse项目 剩下的全部下一步即可 安装tomcat 自己的文章 Javaweb - t…

《云原生安全攻防》-- 构建云原生攻防场景

在本节课程中&#xff0c;我们将学习云原生攻防场景的构建。为了研究云原生安全攻击案例&#xff0c;我们需要搭建一个云原生攻击测试环境&#xff0c;以便进行攻防研究和攻击手法的复现。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; 构建云原生攻防场景&#xf…

设计模式-动态代理

目录 定义 代理模式的优缺点 优点 缺点 应用场景 静态代理 动态代理 相关资料 定义 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它的概念很简单&#xff0c;它通过创建一个代理对象来控制对原始对象的访问。代理模式主要涉及两个…

Spring WebFlux 初探-响应式编程-021

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Nex…

Unity射击游戏开发教程:(17)添加推进器推进和推进器推进动画

添加推进器打开功能 我们可以添加一个推进器栏,用于跟踪玩家使用推进器增强(按住左 Shift 键)的时间。当未使用推力时,将会有一段延迟,直到推力条开始再生。当棒再生时,可以使用推进器,但再生过程将重新开始。 我们将使用 Unity 的 UI Slider 组件,因此我们将其添加到已…

【挑战30天首通《谷粒商城》】-【第一天】10、环境-docker安装mysql

文章目录 课程介绍一、docker 安装 mysql Stage 1&#xff1a;下载镜像文件 Stage 1-1&#xff1a;打开官网查看镜像 Stage 1-2&#xff1a;拉取镜像 Stage 1-3&#xff1a;查看拉取的镜像 Stage 2&#xff1a;创建实例并启动 A&#xff1a;mysql&#xff08;5.7版&#xff09;…

代码随想录—— 填充每个节点的下一个右侧节点指针(Leetcode116)

题目链接 层序遍历 /* // Definition for a Node. class Node {public int val;public Node left;public Node right;public Node next;public Node() {}public Node(int _val) {val _val;}public Node(int _val, Node _left, Node _right, Node _next) {val _val;left _…

【完整过程】Windows下记录PadleOCR训练自己的ocr模型

一、前期准备 1、代码 参考的博主使用的是2.6版本的 博主的paddleocr代码 下面这个是官方的&#xff0c;可能已经更新了&#xff08;我用的是官网当前最新版&#xff09; paddleocr的源代码 注意&#xff1a;最好把上面两个代码都下载下来&#xff0c;后面都会用到 参考博…

HTML静态网页成品作业(HTML+CSS)——自动化专业介绍设计制作(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

从需求到实现:能源软件服务商如何量身定制企业解决方案

能源行业需要数字化转型的原因主要有以下几点&#xff1a;首先&#xff0c;数字化技术可以提高生产效率和安全性&#xff0c;通过实时监控和智能调度降低事故风险&#xff0c;并实现远程控制和自动化生产。其次&#xff0c;数字化转型有助于推动能源行业的创新发展&#xff0c;…

Android ndk获取手机内部存储卡的根目录方法

如下所示&#xff1a; 代码语言&#xff1a;javascript jclass envcls env- FindClass("android/os/Environment"); //获得类引用if (envcls nullptr) return 0;//找到对应的类&#xff0c;该类是静态的返回值是FilejmethodID id env- GetStaticMethodID(envcls,…