FIT2CLOUD
演示站防护 的图标

演示站防护

176
15

限制演示角色在 Halo 控制台执行高风险操作。

演示站防护

演示站防护是一个 Halo 插件,用来限制“演示角色”在 Halo 控制台、用户中心和核心 API 中执行高风险写操作。它适合公开演示站、试用环境、售前演示环境这类场景:访客可以登录体验后台,但不能发布文章、改用户、删资源、上传附件、改主题插件配置,别让人一顿乱点把站点霍霍没了。

功能体验

地址 账号/密码
管理端 demo/demo123.

交流群

点击链接加入群聊【halo博客-lywq插件】

功能介绍

  • 按 Halo 角色识别演示用户:配置项保存的是角色 metadata.name,插件会读取当前登录用户的真实角色绑定。
  • 按 HTTP 方法拦截危险操作:可分别控制 POSTPUTPATCH 写请求和 DELETE 删除请求。
  • 按路径模式保护接口:支持精确路径、* 单段通配、** 多段通配。
  • 支持放行路径:放行路径优先级高于保护路径,用于避免误拦插件自身接口或必须开放的接口。
  • 对前端返回结构化错误:被拦截时返回 403application/problem+json,提示 当前演示用户禁止操作
  • 默认关闭:安装后不会立刻影响管理员操作,需要手动启用。

这个插件是演示环境的兜底防护,不是 Halo RBAC 的替代品。演示账号本身也别乱给超级管理员权限,除非你已经把所有危险接口都配进保护路径里,不然就是往门口挂个锁、后墙留个洞,闹呢。

环境要求

  • Halo >= 2.25.0
  • Java 21+
  • Node.js 18+
  • pnpm

安装教程

  1. 构建插件:
./gradlew build
  1. 构建完成后,在 build/libs 目录找到插件 jar,例如:
build/libs/plugin-demo-guard-1.0.0-SNAPSHOT.jar
  1. 进入 Halo 控制台,打开 插件 页面。

  2. 上传并安装插件 jar。

  3. 启用插件后,进入插件设置页配置防护规则。

升级线上环境时,一定要上传新构建出来的 jar。只改本地代码不升级线上插件,线上当然没反应,这个锅代码不背。

使用教程

最小可用示例:禁止演示用户发布文章

如果你现在只想拦截这个发布接口:

/apis/api.console.halo.run/v1alpha1/posts/019f1b47-2f0e-75fa-a868-d5231f673d24/publish

保护路径应该配置成通配形式:

/apis/api.console.halo.run/v1alpha1/posts/*/publish

同时确认:

启用演示站防护:开启
拦截写请求:开启
演示角色标识:选择演示用户绑定的角色 metadata.name

发布文章接口是 PUT 请求,所以只开“拦截删除请求”没用,这地方别拧巴。

1. 创建演示角色

在 Halo 控制台创建一个专门给演示用户使用的角色,例如 演示角色

插件配置里最终保存的是角色的 metadata.name,通常类似:

role-jlijn3me

不用手填也行,插件设置页的“演示角色标识”会从 Halo 角色列表中选择并保存实际角色名。

2. 给演示用户绑定角色

创建或选择一个演示用户,例如 demo,并给它绑定上一步创建的演示角色。

如果演示用户没有绑定这个角色,插件不会把它当作演示用户处理。

3. 开启防护

在插件设置中配置:

启用演示站防护:开启
演示角色标识:选择演示角色
拦截写请求:开启
拦截删除请求:按需开启

4. 配置保护路径

保护路径只写 URL path,不要写协议、域名、端口和查询参数。

正确:

/apis/api.console.halo.run/v1alpha1/posts/*/publish

错误:

http://localhost:8090/apis/api.console.halo.run/v1alpha1/posts/*/publish

多个路径可以用换行、空格、逗号或分号分隔:

/apis/api.console.halo.run/v1alpha1/posts/*/publish
/apis/api.console.halo.run/v1alpha1/posts/*/unpublish
/apis/api.console.halo.run/v1alpha1/posts/*/recycle

也可以写成一行:

/apis/api.console.halo.run/v1alpha1/posts/*/publish /apis/api.console.halo.run/v1alpha1/posts/*/unpublish /apis/api.console.halo.run/v1alpha1/posts/*/recycle

5. 配置放行路径

放行路径优先级高于保护路径。默认放行插件自身接口:

/apis/console.demoguard.muyin.site/**

不要把放行路径写得太宽,例如:

/apis/**

这会把所有 Halo API 都放掉,防护直接变空气,配置了等于没配置。

配置项说明

配置项 说明 建议
启用演示站防护 总开关,关闭后不拦截任何请求。 配完角色和路径后再开启。
演示角色标识 演示角色的 metadata.name 数组。 使用设置页下拉选择,不要填显示名。
拦截写请求 拦截命中保护路径的 POSTPUTPATCH 演示站通常开启。
拦截删除请求 拦截命中保护路径的 DELETE 演示站通常开启。
保护路径 需要禁止演示用户访问的 API path。 只写 path,支持 ***
放行路径 优先放行的 API path。 保留插件自身接口,别写太宽。

路径匹配规则

  • * 匹配单个路径段。
  • ** 匹配多个路径段。
  • 只匹配 path,不匹配 query string。
  • 放行路径先匹配,命中后不再拦截。
  • GET 请求不会被“拦截写请求”拦截。
  • DELETE 请求只受“拦截删除请求”控制。

示例:禁止演示角色发布任意文章:

/apis/api.console.halo.run/v1alpha1/posts/*/publish

可匹配:

/apis/api.console.halo.run/v1alpha1/posts/019f1b47-2f0e-75fa-a868-d5231f673d24/publish

如果接口带查询参数,例如:

/apis/api.console.halo.run/v1alpha1/posts/xxx/publish?async=true

保护路径仍然只写:

/apis/api.console.halo.run/v1alpha1/posts/*/publish

常用保护路径示例

下面这些路径和插件默认配置保持一致,可以按你的演示站开放范围删减。原则很简单:不想让演示用户改的,就别心软。

文章发布、取消发布、回收:

/apis/api.console.halo.run/v1alpha1/posts/*/publish
/apis/api.console.halo.run/v1alpha1/posts/*/unpublish
/apis/api.console.halo.run/v1alpha1/posts/*/recycle

文章底层资源:

/apis/content.halo.run/v1alpha1/posts/**

用户、角色和权限:

/apis/console.api.security.halo.run/v1alpha1/users/**
/apis/api.console.halo.run/v1alpha1/users
/apis/api.console.halo.run/v1alpha1/users/test/permissions
/apis/api.console.halo.run/v1alpha1/users/-/password
/apis/api.console.halo.run/v1alpha1/users/-
/api/v1alpha1/users/**
/api/v1alpha1/roles/**

附件:

/apis/api.console.halo.run/v1alpha1/attachments/upload
/apis/storage.halo.run/v1alpha1/attachments/**

独立页面:

/apis/content.halo.run/v1alpha1/singlepages/**

主题和插件:

/apis/api.console.halo.run/v1alpha1/themes/*/reset-config
/apis/theme.halo.run/v1alpha1/themes/**
/apis/api.console.halo.run/v1alpha1/plugins/*/plugin-state
/apis/api.console.halo.run/v1alpha1/plugins/*/reset-config
/apis/plugin.halo.run/v1alpha1/plugins/**

系统配置和迁移:

/apis/console.api.halo.run/v1alpha1/systemconfigs/basic
/apis/migration.halo.run/v1alpha1/backups
/apis/console.api.migration.halo.run/v1alpha1/restorations

用户中心安全项:

/apis/uc.api.security.halo.run/v1alpha1/personalaccesstokens
/apis/uc.api.security.halo.run/v1alpha1/authentications/two-factor/settings/enabled
/apis/uc.api.security.halo.run/v1alpha1/authentications/two-factor/totp

不同 Halo 版本或插件可能会调整接口路径。拿不准就打开当前站点的 Swagger:

/swagger-ui/index.html

照真实接口路径配置,别靠猜。尤其是 reset-configresetconfig 这种命名,猜错一个字符就全白搭。

验证是否生效

  1. 使用演示用户登录 Halo 控制台。
  2. 在浏览器开发者工具的 Network 面板确认操作请求的 method 和 path。
  3. 执行受保护操作,例如发布文章。
  4. 预期返回 403,响应体包含 当前演示用户禁止操作
  5. 使用非演示角色账号重复同样操作,应该正常通过。

如果演示用户和管理员都被拦,通常是角色配错或账号角色混了;如果谁都不拦,先看总开关、请求方法、真实 path 和线上 jar 版本。别上来就怀疑玄学,十有八九是配置没对上。

被拦截时的响应

命中防护后,插件返回:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

响应体:

{
  "status": 403,
  "title": "当前演示用户禁止操作",
  "detail": "当前演示用户禁止操作",
  "message": "当前演示用户禁止操作"
}

如果前端仍然只显示 403: Forbidden,说明前端统一错误处理没有读取 detailmessage 字段。后端已经把话递过去了,前端不接,那就是前端自己的脾气。

常见问题

本地有效,线上无效

优先检查这几件事:

  • 线上是否已经安装新构建的 jar。
  • 插件设置里的 enabled 是否为 true
  • 演示用户是否真的绑定了配置中的角色 metadata.name
  • 请求方法是否在拦截范围内,例如发布文章是 PUT,需要开启“拦截写请求”。
  • 保护路径是否只写 path,没有写域名、协议、端口。
  • 放行路径是否过宽,例如误写了 /apis/**
  • 当前线上 Halo 的真实接口路径是否和你配置的一致。

保护路径一整行配置为什么之前不生效

旧版本只按换行和逗号拆分路径。一整行空格分隔会被当成一个超长路径模式,自然匹配不到真实接口。

当前版本已经支持空格、换行、逗号和分号分隔。线上要获得这个能力,必须升级到包含该修复的新 jar。

为什么没进入演示角色判断

通常是当前请求没有可用登录态,或者插件版本还在使用较旧的过滤器链。当前版本在 Halo 安全认证之后读取用户信息,正常登录请求会进入演示角色判断。

为什么配置了角色还是没拦住

确认配置的是角色 metadata.name,不是显示名。显示名可以叫“演示角色”,但真正保存和匹配的一般是:

role-xxxxxxxx

开发约定

新建 Halo 插件必须优先使用官方脚手架:

pnpm create halo-plugin

不要复制已有插件目录改名初始化,容易带入脏配置、缓存和错误依赖。

本地开发

启动 Halo 开发服务:

./gradlew haloServer

前端开发:

cd ui
pnpm install
pnpm dev

热加载插件:

./gradlew reload

运行测试和构建:

./gradlew test
./gradlew build

许可证

GPL-3.0 © Lywq


讨论(0)

这是一个开源应用,我们推荐优先在开源仓库中提交 issue,以便开发者更好地接收和跟进问题。

去提交 issue