<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>Halo - 强大易用的开源建站工具 | 应用市场 | 演示站防护 | 版本发布</title><link>https://www.halo.run</link><atom:link href="https://www.halo.run/feed/app-store/apps/app-zfnjyqga/releases.xml" rel="self" type="application/rss+xml"/><description>Halo - 强大易用的开源建站工具 | 应用市场 | 演示站防护 | 版本发布</description><generator>Halo v2.25.4</generator><language>zh-cn</language><image><url>https://www.halo.run/upload/logo.png</url><title>Halo - 强大易用的开源建站工具 | 应用市场 | 演示站防护 | 版本发布</title><link>https://www.halo.run</link></image><lastBuildDate>Sat, 11 Jul 2026 07:06:08 GMT</lastBuildDate><follow_challenge><feedId>69597013489248256</feedId><userId>41706424548048896</userId></follow_challenge><item><title><![CDATA[演示站防护 1.0.0 发布]]></title><link>https://www.halo.run/store/apps/app-zfnjyqga/releases/app-release-hz8fnyhz</link><description><![CDATA[<img src="https://www.halo.run/plugins/feed/assets/telemetry.gif?title=%E6%BC%94%E7%A4%BA%E7%AB%99%E9%98%B2%E6%8A%A4%201.0.0%20%E5%8F%91%E5%B8%83&amp;url=/store/apps/app-zfnjyqga/releases/app-release-hz8fnyhz" width="1" height="1" alt="" style="opacity:0;">
<h1 id="%E6%BC%94%E7%A4%BA%E7%AB%99%E9%98%B2%E6%8A%A4" tabindex="-1">演示站防护</h1>
<blockquote>
 <p>演示站防护是一个 Halo 插件，用来限制“演示角色”在 Halo 控制台、用户中心和核心 API 中执行高风险写操作。它适合公开演示站、试用环境、售前演示环境这类场景：访客可以登录体验后台，但不能发布文章、改用户、删资源、上传附件、改主题插件配置，别让人一顿乱点把站点霍霍没了。</p>
</blockquote>
<h2 id="%E5%8A%9F%E8%83%BD%E4%BD%93%E9%AA%8C" tabindex="-1">功能体验</h2>
<table>
 <thead>
  <tr>
   <th>地址</th>
   <th>账号/密码</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td><a href="https://demo.muyin.site/console">管理端</a></td>
   <td>demo/demo123.</td>
  </tr>
 </tbody>
</table>
<h2 id="%E4%BA%A4%E6%B5%81%E7%BE%A4" tabindex="-1">交流群</h2>
<p><a href="https://qm.qq.com/q/wuC7NZr0sw">点击链接加入群聊【halo博客-lywq插件】</a></p>
<img src="https://www.halo.run/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2Fbf162401-07fd-49ec-b50f-5218c9510937&amp;size=m">
<h2 id="%E5%8A%9F%E8%83%BD%E4%BB%8B%E7%BB%8D" tabindex="-1">功能介绍</h2>
<ul>
 <li>按 Halo 角色识别演示用户：配置项保存的是角色 <code>metadata.name</code>，插件会读取当前登录用户的真实角色绑定。</li>
 <li>按 HTTP 方法拦截危险操作：可分别控制 <code>POST</code>、<code>PUT</code>、<code>PATCH</code> 写请求和 <code>DELETE</code> 删除请求。</li>
 <li>按路径模式保护接口：支持精确路径、<code>*</code> 单段通配、<code>**</code> 多段通配。</li>
 <li>支持放行路径：放行路径优先级高于保护路径，用于避免误拦插件自身接口或必须开放的接口。</li>
 <li>对前端返回结构化错误：被拦截时返回 <code>403</code> 和 <code>application/problem+json</code>，提示 <code>当前演示用户禁止操作</code>。</li>
 <li>默认关闭：安装后不会立刻影响管理员操作，需要手动启用。</li>
</ul>
<p>这个插件是演示环境的兜底防护，不是 Halo RBAC 的替代品。演示账号本身也别乱给超级管理员权限，除非你已经把所有危险接口都配进保护路径里，不然就是往门口挂个锁、后墙留个洞，闹呢。</p>
<h2 id="%E7%8E%AF%E5%A2%83%E8%A6%81%E6%B1%82" tabindex="-1">环境要求</h2>
<ul>
 <li>Halo <code>&gt;= 2.25.0</code></li>
 <li>Java 21+</li>
 <li>Node.js 18+</li>
 <li>pnpm</li>
</ul>
<h2 id="%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B" tabindex="-1">安装教程</h2>
<ol>
 <li>构建插件：</li>
</ol>
<pre><code class="language-bash">./gradlew build
</code></pre>
<ol start="2">
 <li>构建完成后，在 <code>build/libs</code> 目录找到插件 jar，例如：</li>
</ol>
<pre><code class="language-text">build/libs/plugin-demo-guard-1.0.0-SNAPSHOT.jar
</code></pre>
<ol start="3">
 <li>
  <p>进入 Halo 控制台，打开 <code>插件</code> 页面。</p>
 </li>
 <li>
  <p>上传并安装插件 jar。</p>
 </li>
 <li>
  <p>启用插件后，进入插件设置页配置防护规则。</p>
 </li>
</ol>
<p>升级线上环境时，一定要上传新构建出来的 jar。只改本地代码不升级线上插件，线上当然没反应，这个锅代码不背。</p>
<h2 id="%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B" tabindex="-1">使用教程</h2>
<h3 id="%E6%9C%80%E5%B0%8F%E5%8F%AF%E7%94%A8%E7%A4%BA%E4%BE%8B%EF%BC%9A%E7%A6%81%E6%AD%A2%E6%BC%94%E7%A4%BA%E7%94%A8%E6%88%B7%E5%8F%91%E5%B8%83%E6%96%87%E7%AB%A0" tabindex="-1">最小可用示例：禁止演示用户发布文章</h3>
<p>如果你现在只想拦截这个发布接口：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/019f1b47-2f0e-75fa-a868-d5231f673d24/publish
</code></pre>
<p>保护路径应该配置成通配形式：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
</code></pre>
<p>同时确认：</p>
<pre><code class="language-text">启用演示站防护：开启
拦截写请求：开启
演示角色标识：选择演示用户绑定的角色 metadata.name
</code></pre>
<p>发布文章接口是 <code>PUT</code> 请求，所以只开“拦截删除请求”没用，这地方别拧巴。</p>
<h3 id="1.-%E5%88%9B%E5%BB%BA%E6%BC%94%E7%A4%BA%E8%A7%92%E8%89%B2" tabindex="-1">1. 创建演示角色</h3>
<p>在 Halo 控制台创建一个专门给演示用户使用的角色，例如 <code>演示角色</code>。</p>
<p>插件配置里最终保存的是角色的 <code>metadata.name</code>，通常类似：</p>
<pre><code class="language-text">role-jlijn3me
</code></pre>
<p>不用手填也行，插件设置页的“演示角色标识”会从 Halo 角色列表中选择并保存实际角色名。</p>
<h3 id="2.-%E7%BB%99%E6%BC%94%E7%A4%BA%E7%94%A8%E6%88%B7%E7%BB%91%E5%AE%9A%E8%A7%92%E8%89%B2" tabindex="-1">2. 给演示用户绑定角色</h3>
<p>创建或选择一个演示用户，例如 <code>demo</code>，并给它绑定上一步创建的演示角色。</p>
<p>如果演示用户没有绑定这个角色，插件不会把它当作演示用户处理。</p>
<h3 id="3.-%E5%BC%80%E5%90%AF%E9%98%B2%E6%8A%A4" tabindex="-1">3. 开启防护</h3>
<p>在插件设置中配置：</p>
<pre><code class="language-text">启用演示站防护：开启
演示角色标识：选择演示角色
拦截写请求：开启
拦截删除请求：按需开启
</code></pre>
<h3 id="4.-%E9%85%8D%E7%BD%AE%E4%BF%9D%E6%8A%A4%E8%B7%AF%E5%BE%84" tabindex="-1">4. 配置保护路径</h3>
<p>保护路径只写 URL path，不要写协议、域名、端口和查询参数。</p>
<p>正确：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
</code></pre>
<p>错误：</p>
<pre><code class="language-text">http://localhost:8090/apis/api.console.halo.run/v1alpha1/posts/*/publish
</code></pre>
<p>多个路径可以用换行、空格、逗号或分号分隔：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
/apis/api.console.halo.run/v1alpha1/posts/*/unpublish
/apis/api.console.halo.run/v1alpha1/posts/*/recycle
</code></pre>
<p>也可以写成一行：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish /apis/api.console.halo.run/v1alpha1/posts/*/unpublish /apis/api.console.halo.run/v1alpha1/posts/*/recycle
</code></pre>
<h3 id="5.-%E9%85%8D%E7%BD%AE%E6%94%BE%E8%A1%8C%E8%B7%AF%E5%BE%84" tabindex="-1">5. 配置放行路径</h3>
<p>放行路径优先级高于保护路径。默认放行插件自身接口：</p>
<pre><code class="language-text">/apis/console.demoguard.muyin.site/**
</code></pre>
<p>不要把放行路径写得太宽，例如：</p>
<pre><code class="language-text">/apis/**
</code></pre>
<p>这会把所有 Halo API 都放掉，防护直接变空气，配置了等于没配置。</p>
<h2 id="%E9%85%8D%E7%BD%AE%E9%A1%B9%E8%AF%B4%E6%98%8E" tabindex="-1">配置项说明</h2>
<table>
 <thead>
  <tr>
   <th>配置项</th>
   <th>说明</th>
   <th>建议</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>启用演示站防护</td>
   <td>总开关，关闭后不拦截任何请求。</td>
   <td>配完角色和路径后再开启。</td>
  </tr>
  <tr>
   <td>演示角色标识</td>
   <td>演示角色的 <code>metadata.name</code> 数组。</td>
   <td>使用设置页下拉选择，不要填显示名。</td>
  </tr>
  <tr>
   <td>拦截写请求</td>
   <td>拦截命中保护路径的 <code>POST</code>、<code>PUT</code>、<code>PATCH</code>。</td>
   <td>演示站通常开启。</td>
  </tr>
  <tr>
   <td>拦截删除请求</td>
   <td>拦截命中保护路径的 <code>DELETE</code>。</td>
   <td>演示站通常开启。</td>
  </tr>
  <tr>
   <td>保护路径</td>
   <td>需要禁止演示用户访问的 API path。</td>
   <td>只写 path，支持 <code>*</code> 和 <code>**</code>。</td>
  </tr>
  <tr>
   <td>放行路径</td>
   <td>优先放行的 API path。</td>
   <td>保留插件自身接口，别写太宽。</td>
  </tr>
 </tbody>
</table>
<h2 id="%E8%B7%AF%E5%BE%84%E5%8C%B9%E9%85%8D%E8%A7%84%E5%88%99" tabindex="-1">路径匹配规则</h2>
<ul>
 <li><code>*</code> 匹配单个路径段。</li>
 <li><code>**</code> 匹配多个路径段。</li>
 <li>只匹配 path，不匹配 query string。</li>
 <li>放行路径先匹配，命中后不再拦截。</li>
 <li><code>GET</code> 请求不会被“拦截写请求”拦截。</li>
 <li><code>DELETE</code> 请求只受“拦截删除请求”控制。</li>
</ul>
<p>示例：禁止演示角色发布任意文章：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
</code></pre>
<p>可匹配：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/019f1b47-2f0e-75fa-a868-d5231f673d24/publish
</code></pre>
<p>如果接口带查询参数，例如：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/xxx/publish?async=true
</code></pre>
<p>保护路径仍然只写：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
</code></pre>
<h2 id="%E5%B8%B8%E7%94%A8%E4%BF%9D%E6%8A%A4%E8%B7%AF%E5%BE%84%E7%A4%BA%E4%BE%8B" tabindex="-1">常用保护路径示例</h2>
<p>下面这些路径和插件默认配置保持一致，可以按你的演示站开放范围删减。原则很简单：不想让演示用户改的，就别心软。</p>
<p>文章发布、取消发布、回收：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/posts/*/publish
/apis/api.console.halo.run/v1alpha1/posts/*/unpublish
/apis/api.console.halo.run/v1alpha1/posts/*/recycle
</code></pre>
<p>文章底层资源：</p>
<pre><code class="language-text">/apis/content.halo.run/v1alpha1/posts/**
</code></pre>
<p>用户、角色和权限：</p>
<pre><code class="language-text">/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/**
</code></pre>
<p>附件：</p>
<pre><code class="language-text">/apis/api.console.halo.run/v1alpha1/attachments/upload
/apis/storage.halo.run/v1alpha1/attachments/**
</code></pre>
<p>独立页面：</p>
<pre><code class="language-text">/apis/content.halo.run/v1alpha1/singlepages/**
</code></pre>
<p>主题和插件：</p>
<pre><code class="language-text">/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/**
</code></pre>
<p>系统配置和迁移：</p>
<pre><code class="language-text">/apis/console.api.halo.run/v1alpha1/systemconfigs/basic
/apis/migration.halo.run/v1alpha1/backups
/apis/console.api.migration.halo.run/v1alpha1/restorations
</code></pre>
<p>用户中心安全项：</p>
<pre><code class="language-text">/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
</code></pre>
<p>不同 Halo 版本或插件可能会调整接口路径。拿不准就打开当前站点的 Swagger：</p>
<pre><code class="language-text">/swagger-ui/index.html
</code></pre>
<p>照真实接口路径配置，别靠猜。尤其是 <code>reset-config</code>、<code>resetconfig</code> 这种命名，猜错一个字符就全白搭。</p>
<h2 id="%E9%AA%8C%E8%AF%81%E6%98%AF%E5%90%A6%E7%94%9F%E6%95%88" tabindex="-1">验证是否生效</h2>
<ol>
 <li>使用演示用户登录 Halo 控制台。</li>
 <li>在浏览器开发者工具的 Network 面板确认操作请求的 method 和 path。</li>
 <li>执行受保护操作，例如发布文章。</li>
 <li>预期返回 <code>403</code>，响应体包含 <code>当前演示用户禁止操作</code>。</li>
 <li>使用非演示角色账号重复同样操作，应该正常通过。</li>
</ol>
<p>如果演示用户和管理员都被拦，通常是角色配错或账号角色混了；如果谁都不拦，先看总开关、请求方法、真实 path 和线上 jar 版本。别上来就怀疑玄学，十有八九是配置没对上。</p>
<h2 id="%E8%A2%AB%E6%8B%A6%E6%88%AA%E6%97%B6%E7%9A%84%E5%93%8D%E5%BA%94" tabindex="-1">被拦截时的响应</h2>
<p>命中防护后，插件返回：</p>
<pre><code class="language-http">HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
</code></pre>
<p>响应体：</p>
<pre><code class="language-json">{
  "status": 403,
  "title": "当前演示用户禁止操作",
  "detail": "当前演示用户禁止操作",
  "message": "当前演示用户禁止操作"
}
</code></pre>
<p>如果前端仍然只显示 <code>403: Forbidden</code>，说明前端统一错误处理没有读取 <code>detail</code> 或 <code>message</code> 字段。后端已经把话递过去了，前端不接，那就是前端自己的脾气。</p>
<h2 id="%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98" tabindex="-1">常见问题</h2>
<h3 id="%E6%9C%AC%E5%9C%B0%E6%9C%89%E6%95%88%EF%BC%8C%E7%BA%BF%E4%B8%8A%E6%97%A0%E6%95%88" tabindex="-1">本地有效，线上无效</h3>
<p>优先检查这几件事：</p>
<ul>
 <li>线上是否已经安装新构建的 jar。</li>
 <li>插件设置里的 <code>enabled</code> 是否为 <code>true</code>。</li>
 <li>演示用户是否真的绑定了配置中的角色 <code>metadata.name</code>。</li>
 <li>请求方法是否在拦截范围内，例如发布文章是 <code>PUT</code>，需要开启“拦截写请求”。</li>
 <li>保护路径是否只写 path，没有写域名、协议、端口。</li>
 <li>放行路径是否过宽，例如误写了 <code>/apis/**</code>。</li>
 <li>当前线上 Halo 的真实接口路径是否和你配置的一致。</li>
</ul>
<h3 id="%E4%BF%9D%E6%8A%A4%E8%B7%AF%E5%BE%84%E4%B8%80%E6%95%B4%E8%A1%8C%E9%85%8D%E7%BD%AE%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B9%8B%E5%89%8D%E4%B8%8D%E7%94%9F%E6%95%88" tabindex="-1">保护路径一整行配置为什么之前不生效</h3>
<p>旧版本只按换行和逗号拆分路径。一整行空格分隔会被当成一个超长路径模式，自然匹配不到真实接口。</p>
<p>当前版本已经支持空格、换行、逗号和分号分隔。线上要获得这个能力，必须升级到包含该修复的新 jar。</p>
<h3 id="%E4%B8%BA%E4%BB%80%E4%B9%88%E6%B2%A1%E8%BF%9B%E5%85%A5%E6%BC%94%E7%A4%BA%E8%A7%92%E8%89%B2%E5%88%A4%E6%96%AD" tabindex="-1">为什么没进入演示角色判断</h3>
<p>通常是当前请求没有可用登录态，或者插件版本还在使用较旧的过滤器链。当前版本在 Halo 安全认证之后读取用户信息，正常登录请求会进入演示角色判断。</p>
<h3 id="%E4%B8%BA%E4%BB%80%E4%B9%88%E9%85%8D%E7%BD%AE%E4%BA%86%E8%A7%92%E8%89%B2%E8%BF%98%E6%98%AF%E6%B2%A1%E6%8B%A6%E4%BD%8F" tabindex="-1">为什么配置了角色还是没拦住</h3>
<p>确认配置的是角色 <code>metadata.name</code>，不是显示名。显示名可以叫“演示角色”，但真正保存和匹配的一般是：</p>
<pre><code class="language-text">role-xxxxxxxx
</code></pre>
<h2 id="%E5%BC%80%E5%8F%91%E7%BA%A6%E5%AE%9A" tabindex="-1">开发约定</h2>
<p>新建 Halo 插件必须优先使用官方脚手架：</p>
<pre><code class="language-bash">pnpm create halo-plugin
</code></pre>
<p>不要复制已有插件目录改名初始化，容易带入脏配置、缓存和错误依赖。</p>
<h2 id="%E6%9C%AC%E5%9C%B0%E5%BC%80%E5%8F%91" tabindex="-1">本地开发</h2>
<p>启动 Halo 开发服务：</p>
<pre><code class="language-bash">./gradlew haloServer
</code></pre>
<p>前端开发：</p>
<pre><code class="language-bash">cd ui
pnpm install
pnpm dev
</code></pre>
<p>热加载插件：</p>
<pre><code class="language-bash">./gradlew reload
</code></pre>
<p>运行测试和构建：</p>
<pre><code class="language-bash">./gradlew test
./gradlew build
</code></pre>
<h2 id="%E8%AE%B8%E5%8F%AF%E8%AF%81" tabindex="-1">许可证</h2>
<p><a href="https://www.halo.run/./LICENSE">GPL-3.0</a> © Lywq</p>
<hr>
<p><a href="https://www.halo.run/store/apps/app-zfnjyqga/releases/app-release-hz8fnyhz">查看版本详情</a></p>]]></description><guid isPermaLink="false">store.halo.run/Release/app-release-hz8fnyhz</guid><pubDate>Wed, 1 Jul 2026 07:16:26 GMT</pubDate></item></channel></rss>