📖 1. 背景
Hugo 是一款流行的静态网站生成器,但它不支持原生的密码保护功能。如果你希望某些文章仅限特定用户访问,可以使用 JavaScript 和 MD5 哈希验证来实现前端密码保护。
本方案的目标:
- 避免 Hugo 直接渲染明文密码,防止密码泄露。
- 使用 MD5 哈希存储密码,前端输入明文后哈希比对,提升安全性。
- 使用
sessionStorage
记住验证状态,刷新页面不会重复输入,但关闭页面后自动失效,提升用户体验。
🔐 2. 最终采用的方案
- 文章
.md
文件中存储密码的 MD5 哈希值(而不是明文)。 - 前端 JavaScript 计算用户输入密码的 MD5,并与 Hugo 提供的哈希值比对。
- 如果验证成功,则解锁文章内容,并存入
sessionStorage
,避免重复输入。
🛠 3. 具体实现
📌 3.1 文章 Markdown 文件配置
在 Hugo 文章 .md
文件的 Front Matter 里,存储密码的 MD5 哈希值:
---
title: "私人日记"
date: 2025-02-09
password: "5f4dcc3b5aa765d61d8327deb882cf99" # "password" 的 MD5 哈希
---
🔹 如何获取 MD5 哈希?
在 Windows PowerShell 运行:
$md5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$hash = [System.BitConverter]::ToString($md5.ComputeHash([System.Text.Encoding]::UTF8.GetBytes("password"))).Replace("-", "").ToLower()
Write-Output $hash
✅ 输出:
5f4dcc3b5aa765d61d8327deb882cf99
你也可以用 Python 计算:
import hashlib
print(hashlib.md5("password".encode()).hexdigest())
📌 3.2 修改 single.html
在 Hugo 主题的 layouts/_default/single.html
文件中,添加密码验证逻辑:
{{- define "main" }}
<article class="post-single">
<header class="post-header">
<h1 class="post-title entry-hint-parent">
{{ .Title }}
</h1>
</header>
{{- $password := .Params.password }}
{{- if $password }}
<div id="password-protection" style="text-align: center; padding: 20px;">
<p style="font-size: 1.2em;">🔒 这里是 <strong>秘密基地</strong>!🚀 请输入通关暗号👇</p>
<input type="password" id="post-password" placeholder="✨天王盖地虎✨" />
<button id="check-button" onclick="checkPassword()">🐯 验证一下!</button>
<p id="error-msg" style="color: #f05b72; display: none; font-size: 1em;">❌ 暗号不对!再试试吧~ 🥺</p>
</div>
<div id="post-content" style="display: none;">
{{- if not (.Param "disableAnchoredHeadings") }}
{{- partial "anchored_headings.html" .Content -}}
{{- else }}{{ .Content }}{{ end }}
</div>
{{- else }}
<div id="post-content">
{{- if not (.Param "disableAnchoredHeadings") }}
{{- partial "anchored_headings.html" .Content -}}
{{- else }}{{ .Content }}{{ end }}
</div>
{{- end }}
</article>
<!-- 引入 MD5 计算库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
let storedPassword = sessionStorage.getItem("blog_password_{{ .File.UniqueID }}");
if (!storedPassword) {
document.getElementById("password-protection").style.display = "block";
} else {
unlockContent();
}
});
function checkPassword() {
let inputPassword = document.getElementById("post-password").value;
let correctHash = "{{ .Params.password }}"; // 从 Hugo 读取 MD5 哈希值
let inputHash = CryptoJS.MD5(inputPassword).toString(); // 计算用户输入的 MD5
let errorMsg = document.getElementById("error-msg");
let checkButton = document.getElementById("check-button");
// 按钮增加点击反馈
checkButton.style.transform = "scale(0.95)";
setTimeout(() => {
checkButton.style.transform = "scale(1)";
}, 100);
if (inputHash === correctHash) {
sessionStorage.setItem("blog_password_{{ .File.UniqueID }}", inputHash);
unlockContent();
} else {
// 让错误提示抖动,并显示出来
errorMsg.style.display = "block";
errorMsg.classList.add("shake");
setTimeout(() => {
errorMsg.classList.remove("shake");
}, 300);
}
}
function unlockContent() {
document.getElementById("password-protection").style.display = "none";
document.getElementById("post-content").style.display = "block";
}
// 页面关闭时清空密码
window.addEventListener("beforeunload", function() {
sessionStorage.removeItem("blog_password_{{ .File.UniqueID }}");
});
</script>
{{- end }}
🎯 4. 方案优点
✅ 避免 Hugo 渲染明文密码,只存储 MD5 哈希,防止前端 F12 直接查看。
✅ 用户输入密码时自动计算 MD5 哈希,与 .md
文件存储的哈希比对,安全性更高。
✅ 使用 sessionStorage
记住密码,页面刷新不会重复输入,但关闭页面后自动失效,更安全。
✅ 前端动画优化(输入错误时按钮缩放、错误提示抖动)提升用户体验。
📌 5. 其他替代方案
方案 | 适用场景 | 安全性 | 易用性 |
---|---|---|---|
前端 JavaScript 明文密码 | 简单静态站点 | ❌ 低(F12 可查看) | ✅ 简单 |
Hugo 里存 MD5 哈希,前端比对 | 个人博客 | ✅ 高 | ✅ 中等 |
Nginx Basic Auth | 服务器保护 | 🔒 更高 | ❌ 需 Nginx |
Cloudflare Workers 保护 | CDN 部署 | 🔒 更高 | ❌ 需 Cloudflare |
💡 本方案适合 Hugo 个人博客,提供前端级别的密码保护,同时避免明文密码泄露!
🎉 6. 结论
本方案提供了 简单但安全的 Hugo 文章密码保护,同时兼顾 安全性 和 用户体验。对于需要更高安全性的内容,建议使用 服务器端认证(Nginx/Cloudflare)。
🚀 Enjoy your secure Hugo blogging! 🎩✨