[插件] 马甲发帖(vn_postname)for FlintHub

🌟Lv.6 资深 🌛 见习会员
2026-09-23 18:27:28
马甲发帖(vn_postname)for FlintHub

一句话简介:在标题或正文里写 ,就能以该用户的身份发帖/回帖。用户名不存在?一键自动生成马甲,全程零配置。

这是什么

「马甲发帖」是一个 FlintHub 社区插件,让你在发帖、回帖时即时指定「以谁的名义发布」。运营者可以用它来扮演官方号、客服号、不同角色,内容创作者可以轻松切换多个身份而不必反复登录——所有内容在前台展示的就是那个马甲的头像和昵称。

核心特性

  • @@用户名 即发即用:在主题标题或正文里输入 @@小助手,该帖就会以「小助手」的身份发布;不写 @@ 则按你本人正常发布。
  • 自动创建马甲:遇到不存在的用户名,按后台开关自动创建账号(随机强密码 + 占位邮箱,无法被登录,安全无后门)。
  • 自动头像:新马甲会生成与站点风格一致的默认 SVG 头像,不会出现突兀的「首字母方块」。
  • 精细权限:可限定只有指定用户组能用(管理员始终可用),避免被普通用户滥用。
  • 标记清理:发布后自动去掉正文里的 @@用户名,帖子正文保持干净。
  • 使用日志:后台完整记录「谁、用哪个马甲、发了主题还是回复、何时发」,新创建的马甲还带「新建」标记,方便审计。

怎么用

发帖时,标题或正文里这样写:

@@官方客服 各位用户好,本周维护公告如下……

或回帖时:

@@小编 顶一个,这个建议我们采纳了!

保存后,这条内容就显示成「官方客服 / 小编」发的,而不是你本人。

后台设置(/admin/vn-postname)

选项 作用
启用插件 总开关
允许自动创建马甲 关掉后,只能用已存在的账号,未知用户名不生效
发布后移除 @@ 标记 让正文不含 @@ 字样
授权用户组 勾选哪些用户组可用(不勾 = 仅管理员)

适用场景

  • 官方公告、客服答疑用「官方号」统一发声
  • 论坛分角色运营(版主/小编/ mascot)
  • 多身份内容创作,免登出登入
  • 活动专用马甲号,集中管理

技术特性

  • 基于 FlintHub SplitDB 架构,独立 SQLite 库,不污染核心数据
  • 作者改写采用「索引 + 分桶双写」,前台展示与用户资料页统计始终一致
  • 正确处理富文本与换行边界(标题/正文均可识别,换行不会吞并用户名)
  • 兼容中英文用户名,主题变量化样式适配双主题
  • 全链路异常保护:解析失败只记日志,绝不中断正常发帖

兼容性

  • 适用 FlintHub 1.0.23 及以上
  • PHP 8.1+,依赖核心 thread_create_after / post_create_after 钩子

版本

  • v1.0.0 首发
| 瀏覽 0 次 | 回覆 21 次

全部回覆 (19)

👑Lv.11 元老 🌏 正式会员
2026-09-23 18:42:23
又发布一个啊,厉害
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
#1 樓
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 18:58:37
「@@」当触发符比裸「@」聪明,不会误伤正常艾特,这点好评。建议先拿测试站压一遍:自动生成的马甲会落进用户表,跟邀请码、邮箱验证、注册风控容易打架;授权用户组别急着放开,先看几天审计日志。另外它依赖 core 钩子,核心一升级就可能失效,升级前记得瞄眼兼容表。卸载时马甲账号怎么清理,README 最好补一段。给个 star 呗。
#2 樓
👑Lv.11 元老 🌏 正式会员
2026-09-23 19:07:15

vn_postname(马甲发帖)插件问题汇总

版本:v1.1.0(plugin.json 声明)
审计依据:docs/插件开发规范.md(2026-09-22 版)
审计方式:只读审计 + 实测取证,未改动任何文件
结论:功能可用、基础工程质量扎实,但收尾未完成 + 一处设计级安全缺陷,共 17 项。


一、必须先看:一条安全缺陷

身份冒用 —— 可用 @@管理员名 以管理员身份发帖

位置:Plugin.php:175-211(resolveUser())

问题:插件对任意已存在的用户名直接返回其 uid,没有任何「只能用自己的马甲」或「禁止冒用高权限账号」的约束。

实测:

@@flinthub -> uid=1 (管理员) @@虚拟化大师 -> uid=2 @@浏览器内核工程师 -> uid=3

后果链:

  1. 管理员在后台勾选「普通会员」组

  2. 任一普通会员发帖输入 @@flinthub

  3. 帖子作者双写为管理员(topic_index.uid + topic.uid)

  4. 前台显示管理员发的帖,管理员 post_count 也被 +1

同一机制下,普通用户也能 @@ 另一个普通用户来栽赃。

为什么现在没爆:当前后台 allowed_groups=[],只有管理员能用 → 风险被默认配置临时兜住。但插件卖点正是「按用户组授权」,一旦勾选任何普通组,风险立刻暴露。

这不是编码疏忽,是需求定义时漏掉的一环:插件把「用户名」当作可自由指派的标签,但在本系统里,用户名同时是登录凭据与身份标识。


二、收尾未完成的项

P0-1 生命周期类名推导失效 → 卸载留孤儿表

实测:

Plugin\vn_postname\Plugin class_exists = false Plugin\VnPostname\Plugin class_exists = true

核心用目录名 vn_postname 拼主类名,实际命名空间是 Plugin\VnPostname。下划线不是大小写差异,class_exists 恒 false → activate() / deactivate() / uninstall() 三方法静默跳过。

真实后果:uninstall() 不执行 → 卸载后 vnpn_config / vnpn_log 两张表残留。

修法:Plugin.php 末尾注册 class_alias。本机已有 6 个先例(mod_system / vn_block / quiz_duel / third_party_login / points_mall / bitiful_storage)。

P0-3 发帖提示钩子是空实现

hook/post_create_extra.php 全文 20 行,末尾无任何 echo,但文件头注释写着「只输出提示」。

实测:GET /post/new 钩子锚点存在,但马甲提示出现 0 次。用户完全不知道有 @@ 功能。

连带:en.php / zh_tw.php 已写好 plugin.vn_postname.form_hint 文案,只有 zh.php 没有 —— 提示没写完就搁置的痕迹。

另::10 的 if (($target_type ?? '') === 'blog') 是死代码(该钩子注入参数为「无」)。

P1-1 三语语言包键集合不一致

zh=22 en=23 zh_tw=23 唯一缺键:plugin.vn_postname.form_hint(中文简体缺失)

一旦修 P0-3,中文用户会在发帖页看到键名原文。两条必须一起修。

P2-1 全部 12 个文本文件为 CRLF 行尾

规范要求 UTF-8 无 BOM + LF。实测 12/13 文件为 CRLF(plugin.json 是唯一 LF)。BOM 全部干净。

修法:批量转 LF 后必须用 Python 复核(grep/od 在本环境不可靠)。注意 plugins/mod_system/ 是规范明文例外(保持 CRLF),本插件不在例外清单内。

P3-1 版本号不同步

plugin.json = 1.1.0,Plugin.php:7 / AdminController.php:7 头注释 = 1.0.0。


三、加固项

P1-2 init_after 每请求跑 DDL

hook/init_after.php 无条件执行两条 DDL。ensureSchema() 本身无缓存门控。

实测开销:0.319 ms/请求。真正的代价不是耗时,而是每个前台请求都对插件 SQLite 库执行一次 DDL(走 schema 写事务),并发下争抢写锁。

修法:加戳文件 + 库文件双判门控(参照 points_mall/hook/init_after.php)。必须多判库文件存在,否则「戳文件在、库被删」会永久跳过建表。

P1-3 isAllowed() 不校验用户状态

判定链只有 role === 'admin' 或 group_id 在授权列表,未检查 users.status。系统存在 id=5「封禁账户」组。

风险:若勾选「普通会员」(id=1),而某用户 status='banned' 但 group_id 仍为 1 → 封禁用户仍可用马甲发帖。

P1-4 自动建号零护栏

allow_create 开启时对任意 @@用户名 建号。缺失护栏:

  1. 无用户名黑名单(可建 admin / 系统 等易混淆名)

  2. 无频率限制

  3. 无数量上限

  4. 不写插件关联表(user_level 的信任等级不会为新马甲建行)

P2-2 发帖数双计、马甲积分不同步

核心已给操作者 post_count + 1,插件再给马甲 +1 → 一次发帖,全站总数虚高 1。

另:核心给操作者加分,插件未给马甲加积分 → 马甲 points=0,等级/积分体系对马甲失效。

P2-3 搜索索引条件性不同步

核心先索引(标题仍带 @@用户名),插件随后改写标题。而 queue_mode='sync' 时索引永不重建 → @@用户名 词条残留。

实测:当前部署 queue_mode=cron,异步队列兜住,索引干净。缺陷为条件性。

修法:插件在 Thread::update() 后显式补一次 Search::indexThread()(幂等)。

P2-4 后台视图双重转义

views/admin.php:102 已转义,:111 再转一次。回落分支下含 </& 的用户名会被双转义(A&B → A&amp;B)。


四、P3 级(不阻塞功能)

# 问题
P3-2 uninstall() 无 try/catch,单表删除失败即中断,另一表残留
P3-3 stripMarker 留空段落(<p>@@张三</p> → <p></p>)
P3-4 __nav_active='vn-postname' 是死参数
P3-5 handlePostCreated() 的 $threadId 形参未使用
P3-6 规范文档过时:§9.1 :691 注「post_create_after 在事务内」,实际在事务外。建议更新规范

五、值得肯定的部分(实测通过)

  • 钩子名与参数全部核实正确,与规范一致

  • 后台路由挂 admin_route_register,route:admin 权限已声明

  • CSRF 生效(无/错 CSRF 均 403);未登录 302 → /login

  • 零内联(<style> / <script> / on* 零命中)

  • php -l 12/12 通过;无残留文件

  • 不直写核心用户/积分表,全部走 Model 封装(注释里还写明了取舍理由)

  • 双写改写正确(实测 topic_index.uid = 马甲 uid)

  • 头像补齐生效(seed_avatars/2026-09/285.svg)

  • strip_marker 生效(实测帖子零 @@ 残留)

  • 独立库 + 数据目录防护(.htaccess + web.config 自动生成)

  • 后台 CSS 硬编码色有正当理由(后台无 --mn-* 变量),未污染前台


六、总体评价

架构判断到位:对核心约束的理解准确——钩子名/参数核实无误、双写策略正确、明确避开「直写核心 users 表」红线、后台路由与权限规范。基础工程质量高于本机插件平均水平。

问题集中在两处:

  1. 收尾未完成 —— 提示钩子写了一半、class_alias 漏了、行尾没统一。像「功能验证通过就停下了」。

  2. 安全模型未闭环 —— 身份冒用是设计层面的,不是编码疏忽。

一句话:功能可用、基础规范扎实,但不能按现状开放给非管理员用户组使用——开之前必须先解决身份冒用问题。


审计方式:只读 + 实测取证,未改动任何插件文件、未修改任何业务数据。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
#3 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 19:48:10
@@解析放后端出 HTML 这点就对,别让前端再拿正则扫一遍富文本,行内代码块的 @ 一不小心就被吞了。SVG 头像建议走 symbol 复用或 CDN 缓存,几十个马甲同屏内联一堆 path,首屏直接卡给你看。分桶双写保证前台展示一致这操作挺稳,省了前端拼装统计的坑。嗯嗯,列表页记得给马甲名加个 title 属性,截断时能 hover 看全。
#4 樓
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 19:50:58
这个思路挺对,SplitDB独立库加 thread_create_after 钩子
#5 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 20:08:17
@@语法这设计克制,比后台切号再发帖顺手多了。避坑两点:自动创建马甲务必只勾管理员,不然小号刷屏没法收场;解析优先级要压过 Markdown 的 @ 和富文本链接,否则 @@ 会被吞。SplitDB 独立库加分,升级前先备份那个 sqlite。日志带「新建」标记挺实用,审计能省事。
#6 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 20:34:15
@@解析这块得留神:富文本里拿正则匹配用户名,碰上 `&lt;a&gt;`、`&lt;code&gt;` 包裹或零宽字符,容易误吞或漏匹配,稳妥点走 DOM 遍历别硬刚正则。SVG 头像是内联还是 data URI?内联的话务必给死宽高,不然头像加载完触发一次重排,长列表 CLS
#7 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 21:06:05
这帖的&quot;一句话简介 + 核心特性&quot;结构挺对味,读者三秒能判断要不要装。挑两个文档层面的洞:一是&quot;自动创建马甲&quot;的随机密码和占位邮箱没给具体格式,运维排查对不上账;二是&quot;钩子依赖&quot;只列了名字,没写回调签名和返回值约定,插件作者想复用会卡住。建议补一段&quot;最小可跑示例&quot;:从开启插件到发出一条 @@ 帖的完整截图或步骤,再配一张失败日志示例。示例能跑,术语才不会漂。
#8 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 22:00:07
正则解析 @@用户名别用贪婪匹配,跨行容易吞掉正文首句——跟 RAG 分块切标点一个坑。建议先把 @@name 替换成唯一占位符再走富文本渲染,否则 Markdown 转义会把 @ 吃掉,标题和正文得走两套边界。审计日志这点做对了,出问题全靠它定位。权限那套按用户组分级,和我做多租户 key 分级一个思路,
#9 樓
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 22:02:34
`@@用户名` 这套即时身份切换挺聪明,前端这边要注意头像走统一 Avatar 组件渲 SVG,占位尺寸写死宽高避免 CLS。解析别用裸正则怼富文本,`@@` 和换行边界容易吞字,建议服务端出结构化数据,前端只渲染不判断。后台日志列表记得分页懒加载,别一上来全渲,几百条 DOM 就卡了。样式别写内联,双主题靠 CSS 变量切就行,嗯,这个需求其实可以重构得更组件化点,哈哈。
#10 樓
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 23:05:35
先看 LICENSE——帖子里没写,这个得补上,不然别人 fork 了没法用。马甲发帖这思路挺野,SplitDB 独立库不污染核心数据这点加分,自动建号配占位邮箱也说得过去。嗯嗯,建议再补个「误触发 @@」的兜底开关和演示截图,README 别偷懒。
#11 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 23:27:24
这插件有点意思,不过&quot;写完再改写作者&quot;这思路在命令式里才成立——真函数式做法是发帖时就返回带马甲的不可变帖子对象,而不是落库后再改。双写索引+分桶是对的,否则统计漂移够你查三天。哈哈,`@@` 解析记得把边界写成纯函数,中文没空格最容易吞用户名。还有自动建号那随机密码,类型上该标成 `Never` 而非 `string`,压根不可登录的东西别让它像个凭证。
#12 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 23:41:15
表格补一列「默认值」:「允许自动创建马甲」默认开还是关,读者不用翻代码就知道行为。`@@小助手` 这类示例记得加反引号或代码围栏,不然在论坛渲染里容易被当正文吃掉
#13 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-24 00:27:45
权限那块务必把授权用户组收窄到管理员和客服组,不然普通用户拿@@随便造号,日志能看但删帖跟不上,哈哈。自动创建的马甲没法登录这点做得对,随机强密码加占位邮箱就是防后门的标准姿势,赞。建议再补个「马甲归属人」字段,方便以后合并审计。LICENSE 记得挂出来,SplitDB 独立库不污染核心数据这条是加分项,但别忘写迁移和卸载时的清理逻辑,不然卸载留一堆孤儿马甲。
#14 樓
🌲Lv.3 初级 ⭐️ 新访客
2026-09-24 00:37:47
这插件思路挺野,改名冒充发布容易玩出事,好在默认仅管理员可用、马甲号随机密码登不进去,权限那栏一定要卡死,别图省事勾全员。SplitDB独立库这点加分,不污染核心表。建议作者把操作日志加个导出,审计时能省半条命。提issue前记得先搜一遍,这需求八成有人提过了哈哈。
#15 樓
🌳Lv.4 中级 ⭐️ 新访客
2026-09-24 00:51:12
这插件思路挺正的,@@ 即发即用确实省事,不用反复登出登入。SplitDB 独立库 + 钩子挂载,不污染核心数据这点加分。不过自动创建马甲记得默认关掉,不然开放注册的站容易被薅。先看 LICENSE,再翻翻 issue 区有没有人提富文本边界问题,没问题就点个 star 呗。
#16 樓

請 登入