防垃圾评论安全防护
详情介绍
Akismet 防垃圾评论插件
在社区发帖 / 回帖前置拦截垃圾评论、广告灌水、恶意链接。集成 Akismet 云端 API + 本地规则引擎 双层防御,Akismet 不可用时自动降级为本地过滤,7×24 小时守护社区纯净。
✨ 核心特性
| 特性 | 说明 |
|---|---|
| 零侵入 | 通过 ThinkPHP 全局中间件拦截,不修改 community 核心代码 |
| 自动降级 | Akismet API 超时 / 5xx / 未配置时自动跳过,不阻断正常发帖 |
| 短路求值 | 本地规则先跑(毫秒级),命中即拦截,不浪费 Akismet API 额度 |
| 智能豁免 | 管理员会话、VIP 会员、注册 N 天老用户可分别豁免检测 |
| 拦截日志 | 自动记录到数据库,后台可查看、批量清理 |
| 一键验证 | 后台实时验证 Akismet API Key 有效性 |
| 自动读取 | 首次安装时自动识别当前站点 URL,开箱即用 |
| 默认关键词 | 内置 82 个常见垃圾关键词(代写、刷单、套现、博彩、色情等) |
📦 安装
插件目录结构:
plugins/Akismet/
├── plugin.json
├── AkismetPlugin.php # 主类(配置 / 自动建表 / 常量)
├── middleware.php # 全局中间件声明
├── routes.php # 后台路由
├── install.sql # 日志表 SQL(首次启用自动建表)
├── icon.svg
├── middleware/
│ └── AkismetGuard.php # 核心拦截中间件
├── service/
│ ├── AkismetClient.php # Akismet 云端 API 客户端
│ ├── LocalFilter.php # 本地规则引擎
│ └── FilterLog.php # 拦截日志(记录 / 查询 / 清理 / 统计)
├── controller/admin/
│ ├── Base.php
│ └── Index.php # 后台配置 + 日志管理
└── view/admin/
├── config.html # 配置页
└── logs.html # 拦截日志列表步骤
- 将
Akismet/文件夹放到plugins/目录下 - 清除 runtime 缓存让中间件编译生效:
```powershell
Remove-Item -Recurse -Force runtime\cache\* -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force runtime\temp\* -ErrorAction SilentlyContinue
```
- 重启 Nginx(ThinkPHP 中间件注册在路由加载阶段,必须重启)
- 进入后台 → 插件管理 → 找到「垃圾评论拦截」→ 安装并启用
- 首次启用会自动创建
zsensakismetlog日志表,无需手动执行 SQL
⚙️ 后台配置
后台路径:/admin/akismet
基本设置
| 配置项 | 默认值 | 说明 |
|---|---|---|
| 启用防垃圾检测 | ✅ 开 | 总开关 |
| 记录拦截日志 | ✅ 开 | 每次拦截写入数据库,便于事后审查 |
| 拦截模式 | 直接拒绝 | reject = 禁止发布;hold = 允许但标记待审 |
Akismet 云端检测(可选但推荐)
在 https://akismet.com/ 注册免费账号,获取 API Key。
| 配置项 | 默认值 | 说明 |
|---|---|---|
| 启用 Akismet | ✅ 开 | 关闭后仅使用本地规则引擎 |
| API Key | 空 | 在 akismet.com 注册后获得 |
| 站点 URL | 自动读取 | 后台配置表的 site_domain → 当前请求 domain → config('app.url') |
后台有一个「🔄 自动」按钮,点击即可一键填入检测到的当前站点地址。
本地规则引擎(始终生效)
| 配置项 | 默认值 | 说明 |
|---|---|---|
| 垃圾关键词 | 82 个内置 | 每行一个,忽略大小写子串匹配;可自由增删 |
| 最大链接数 | 5 | 内容中 URL 数量超限即垃圾;设 0 不限 |
| 最小有效字符数 | 2 | 去掉标点/空白/表情后的纯有效文本;设 0 不限 |
| 拒绝无意义内容 | ✅ 开 | 纯表情 / 纯标点 / 纯空白直接拦截 |
豁免白名单
| 配置项 | 默认值 | 说明 |
|---|---|---|
| 管理员免检测 | ✅ 开 | 超管后台会话跳过所有规则 |
| VIP 会员免检测 | ❌ 关 | 需安装 vip 插件 |
| 注册 N 天后免检测 | 0 | 老用户信任白名单;设 0 不开启 |
🛡️ 拦截流程
社区 POST /topic/save 或 /reply/save
↓
AkismetGuard 全局中间件
↓
① 仅拦截 POST + pathinfo 匹配 topic/save 或 reply/save
(其他请求立即 next(),零开销)
↓
② 豁免检查 → 管理员 / VIP / 老用户 命中即放行
↓
③ 本地规则引擎(毫秒级)
├─ 垃圾关键词黑名单
├─ URL 链接数量超限
├─ 纯表情 / 纯标点(无意义)
└─ 有效字符长度过短
↓ 未命中
④ Akismet 云端 API(5s 超时)
POST https://rest.akismet.com/1.1/comment-check
api_key / blog / user_ip / comment_content / comment_type
↓ 返回 true
写入拦截日志 → 返回 JSON 错误 → 用户看到:
"内容被安全系统判定为垃圾,请修改后重试:命中垃圾关键词代写论文"短路设计
本地规则命中即拦截,不会再调用 Akismet API,既快又省额度。
降级设计
| Akismet 状态 | 行为 |
|---|---|
| API Key 未填 | 跳过云端,仅用本地规则 |
| API Key 已填但超时 | 跳过云端,本次请求放行(不阻断正常发帖) |
| HTTP 5xx / 403 | 跳过云端,降级为本地规则 |
返回 invalid | 跳过云端(Key 失效,自动降级) |
安全优先还是可用优先? 本插件选择「可用优先」——云端故障时不拦截,避免误伤正常用户。本地规则始终生效兜底。
📋 默认垃圾关键词(82 个)
代写论文、毕业论文代写、代发论文
免费领取、限时领取、点击领取
加QQ私聊、微信私聊、扫码联系
点击跳转链接、点击下方链接、复制打开浏览器、手机版直接搜、百度搜索一下
无需下载、一键生成、AI生成
免费试用、免费领取VIP、永久VIP
破解版、中文版破解、绿色版
软件下载、游戏下载
私服、外挂、作弊器、辅助工具
博彩、赌博、在线娱乐
亚洲男人天堂、色情、AV
交友聊天、约炮、一夜情、招嫖
办证、刻章、发票
高利贷、小额贷款
网络兼职、日赚、无风险、稳赚不赔
刷单、淘宝刷单、京东刷单、代刷单
代刷信誉、代刷好评、代刷收藏、代刷粉丝
代刷播放量、代点关注、代投票、代砍价
代抢火车票、代抢票
手机定位、通话录音、监听软件、手机窃听
信用卡套现、花呗套现、白条套现、网贷
APP下载链接、点击下载APP、打开APP
推广链接、佣金、返佣、高佣金、拉新、推广后台关键词框支持自由增删,每行一个。想恢复默认 → 清空保存(系统会把空值合并回默认值)。
🔍 拦截日志
后台路径:/admin/akismet/logs
日志字段:
| 字段 | 说明 |
|---|---|
| 时间 | 拦截发生时间 |
| 来源 | 发帖 / 回帖 |
| 用户 | 用户昵称(游客显示为 "游客") |
| IP | 提交者 IP |
| 拦截原因 | 命中的关键词 / 链接数超限 / Akismet 判定等 |
| Akismet | 云端判定 = Akismet 返回 true;本地规则 = 本地引擎拦截 |
| 内容预览 | 被拦截内容前 500 字 |
清理操作:
- 清空全部日志
- 删除 30 天前的日志
- 删除 7 天前的日志
🔧 开发者接口
手动检测
use plugins\Akismet\service\LocalFilter;
use plugins\Akismet\service\AkismetClient;
use plugins\Akismet\AkismetPlugin;
$cfg = AkismetPlugin::config();
// 仅本地规则
$reason = LocalFilter::check($text, $cfg);
if ($reason !== null) {
// 命中垃圾
}
// 调 Akismet
$result = AkismetClient::check(
apiKey: (string) $cfg['akismet_api_key'],
blogUrl: (string) $cfg['akismet_blog_url'],
content: $text,
ip: '1.2.3.4',
userAgent: $_SERVER['HTTP_USER_AGENT'] ?? '',
commentType: 'comment', // 或 'post'
);
// true = 垃圾 false = 非垃圾 null = 调用失败
// 验证 API Key
$ok = AkismetClient::verifyKey($apiKey, $blogUrl);日志查询
use plugins\Akismet\service\FilterLog;
$result = FilterLog::recent($page = 1, $pageSize = 20);
// ['total' => N, 'page' => N, 'pages' => N, 'list' => [...]]
$summary = FilterLog::summary();
// ['total' => N, 'today' => N, 'topic' => N, 'reply' => N]数据库表
CREATE TABLE zsens_akismet_log (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
created_at INT UNSIGNED NOT NULL DEFAULT 0,
source VARCHAR(20) NOT NULL DEFAULT '', -- 'topic' | 'reply'
action VARCHAR(20) NOT NULL DEFAULT '', -- 'reject'
reason VARCHAR(255) NOT NULL DEFAULT '', -- 拦截原因
user_id INT NOT NULL DEFAULT 0,
username VARCHAR(100) NOT NULL DEFAULT '',
ip VARCHAR(45) NOT NULL DEFAULT '',
content_preview VARCHAR(500) NOT NULL DEFAULT '',
akismet_result VARCHAR(20) NOT NULL DEFAULT '', -- 'true' | ''
KEY idx_created_at (created_at),
KEY idx_user_id (user_id),
KEY idx_source (source)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Akismet防垃圾拦截日志';首次启用时由 AkismetPlugin::ensureSchema() 自动创建,表前缀跟随系统 config('database.connections.mysql.prefix')。
🏗️ 工作方式说明
为什么用全局中间件而不是 TopicPluginHooks?
- TopicPluginHooks::afterSave 是帖子入库之后才触发,拦截时垃圾内容已经存进数据库 —— 无法达到防垃圾的目的
- 全局中间件在请求进入控制器之前拦截,可以直接返回 JSON 错误给前端,帖子根本不会入库
- 拦截目标通过
pathinfo后缀匹配:
```
/topic/save → 拦截
/reply/save → 拦截
其他所有路由 → 直接放行(零开销)
```
为什么 Akismet 故障时不拦截?
Akismet 官方 API 并非 100% 可靠(偶有超时 / 5xx)。如果把云端故障视为垃圾,会导致正常用户发帖大面积失败。所以本插件策略是:
本地规则永远生效,云端失败自动跳过 — 宁可少拦一两条垃圾,不能误伤一篇正常内容。
如果希望更严格,可在后台只启用「本地规则引擎」,关闭 Akismet 并加强关键词列表。
🐛 常见问题
Q: 安装后社区发帖 / 回帖报错「内容被安全系统判定为垃圾」
说明你当前提交内容命中了本地关键词或 Akismet 判定。可在后台查看拦截日志定位原因。
Q: Akismet 一直返回 API Key 无效?
- 确认 Key 是在 akismet.com 注册后获取的
- 站点 URL 必须与 Key 绑定的域名一致(或同属一个主域)
- 后台填写的 URL 必须带
https://前缀,结尾不要带/
Q: 拦截误杀了正常内容怎么办?
- 从关键词黑名单里移除对应关键词
- 临时在配置里关闭「启用防垃圾检测」紧急放行
- 考虑把「豁免白名单」里的 VIP / 老用户打开
Q: 性能影响?
- 中间件匹配:仅
pathinfo字符串比较,纳秒级 - 本地过滤:纯 PHP 正则 + 字符串操作,单条 < 1ms
- Akismet API:约 200~500ms,5s 超时;只有前两层都放行才会调用
📜 License
由 kuakeba.cn 开发,保留所有权利。
更新记录 (2)
-
v1.0.01
2026-10-28
修复检测逻辑
-
v1.0.0
2026-10-28
正式发布
微信客服
QQ官方交流群