跳到正文
世傲命的技术观测站 PERSONAL TECHNOLOGY OBSERVATORY
文章 前端实践 2026-02

文章可见性控制演示

演示静态博客中公开、私密、需密码三种文章可见性状态的设计思路、实现方式和安全边界。

这篇文章记录一个静态博客里的文章可见性控制方案。目标是在没有数据库、没有登录系统、没有服务端鉴权的前提下,把文章分成公开、私密和需密码三种状态。

先说结论:静态站可以做“内容分发层面的可见性控制”,但不能做真正的权限系统。任何需要强安全的内容,都不应该只靠前端脚本保护。

静态博客文章可见性模型

三种状态

状态说明适用场景
公开所有人可见技术分享、项目复盘、教程
私密只在源码中保留,不进入公开页面个人笔记、草稿、未完成内容
需密码有公开入口,但正文需要输入密码后查看轻量内部分享、临时资料

这三个状态的核心区别,不是按钮样式,而是生成阶段如何处理文章。

Frontmatter 设计

公开文章可以不写 visibility,也可以显式声明:

---
title: 文章可见性控制演示
date: 2026-02-12 19:30:00
tags: [Hexo, 教程]
visibility: public
---

私密文章不应进入首页、归档、搜索和 sitemap:

---
title: 我的私密日记
date: 2026-02-12 20:00:00
tags: [日记]
visibility: private
---

需密码文章可以保留公开入口,但正文需要被保护:

---
title: Vue3 高级教程会员版
date: 2026-02-12 21:00:00
tags: [Vue, 高级教程]
visibility: password
passwordHash: "sha256-hash"
---

不要把明文密码写进公开生成产物。即使是 hash,也只能用于轻量门禁,不等于真正安全。

生成阶段过滤

静态站没有运行时数据库,所以最关键的控制点在构建阶段。

私密文章应该在生成列表时被过滤掉:

function isPublicPost(post) {
return (post.visibility ?? "public") !== "private";
}
const visiblePosts = allPosts.filter(isPublicPost);

需要过滤的位置包括:

  • 首页文章列表。
  • 归档页。
  • 标签页。
  • 分类页。
  • RSS。
  • sitemap。
  • 搜索索引。

只从首页隐藏是不够的。如果文章仍然进入 sitemap 或搜索索引,依然可能被发现。

密码门禁

前端密码门禁只能做轻量访问提示。示例结构:

<div class="password-gate" data-hash="hashed-password">
<input type="password" placeholder="请输入访问密码" />
<button type="button">查看文章</button>
</div>

验证逻辑可以用 Web Crypto 计算 SHA-256:

async function verifyPassword(input, expectedHash) {
const digest = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(input)
);
const hash = [...new Uint8Array(digest)]
.map(byte => byte.toString(16).padStart(2, "0"))
.join("");
return hash === expectedHash;
}

但需要明确:如果正文已经被生成到 HTML,只是用 CSS 或 JS 隐藏,那就不安全。真正要提高保护程度,至少要在构建阶段把正文加密成密文,前端输入密码后再解密。

安全边界

静态博客的可见性控制适合:

  • 草稿不进入公开列表。
  • 内部资料做临时分享。
  • 给读者一个访问提示。
  • 轻量会员内容演示。

不适合:

  • 保存真实敏感信息。
  • 发布密钥、token、私钥。
  • 保护商业机密。
  • 做真正的用户权限系统。

真正需要权限控制的内容,应该放到有服务端鉴权的系统里,例如带登录、数据库、访问日志和权限校验的应用。

迁移到 Astro 后怎么做

Astro 的内容集合可以在 schema 层定义字段,例如:

visibility: z.enum(["public", "private", "password"]).default("public")

然后在获取文章列表时统一过滤:

const posts = await getCollection("posts", post => {
return post.data.visibility !== "private" && !post.data.draft;
});

这样比在模板里到处写判断更稳。生成 RSS、sitemap、搜索索引时也要复用同一套过滤逻辑。

复盘

这套方案的核心是区分“公开入口控制”和“内容安全保护”。静态博客可以很好地控制文章是否出现在列表、归档和索引里;但只要内容已经被生成到前端,任何人都有可能通过源码或网络请求看到。

所以,我更推荐把它作为内容管理能力,而不是安全能力。需要安全时,上服务端鉴权;需要写作管理时,用 frontmatter 控制生成结果。

继续探索

展开系列路线

同类延伸

前端实践

02 / 2
查看完整路线
  1. 01把书签做成 Obsidian 风格知识图谱:节点、连线和交互技巧
  2. 02文章可见性控制演示当前
  • #Hexo
  • #Butterfly
  • #JavaScript
  • #可见性控制

COMMUNITY DISCUSSION

评论

使用 GitHub 账号登录后参与讨论,评论会同步到 GitHub Discussions。