📚 离职知识库

站点访问密码门 · 跨子域 SSO 机制

给一批私人动态站加的统一登录门,跨子域单点登录。本篇只讲机制与经验教训,不含任何密码、密钥、子域清单。

  • 一套共享的「访问密码 + 签名密钥」环境变量:SITE_PASSWORD(口令)+ AUTH_SECRET(HMAC 签名密钥)。
  • 登录流程(Next.js):
    1. proxy.ts(中间件)拦截未登录请求 → 重定向到 /login
    2. /login 页提交密码 → /api/login 校验密码;
    3. 校验通过后用 AUTH_SECRET 签发一个 HMAC 令牌,写进 httpOnly Cookie
    4. 后续请求带 Cookie,中间件验签放行。
  • 跨子域 SSO 的关键:Cookie 的 Domain 设为父域(如 .example.com),这样同父域下所有子站共享登录态,登录一次、全站免登录(有效期如 30 天)。
  • 核心库集中在一个文件(如 lib/hub-auth.ts / lib/auth.ts),4 个门文件即可构成完整的门:lib/hub-auth.tsproxy.tsapp/login/page.tsxapp/api/login/route.ts

#二、给新站加门(3 步)

  1. 把这 4 个门文件拷进新项目(src/app 结构的放 src/ 下): lib/hub-auth.tsproxy.tsapp/login/page.tsxapp/api/login/route.ts
  2. 在部署平台配置两个环境变量 SITE_PASSWORDAUTH_SECRET——要 SSO 生效,所有同父域站点这两个值必须完全一致
  3. 部署上线,并确保这 4 个门文件都进了 git 提交(走 Git 自动部署时尤其要确认,别把门文件漏掉、把门冲掉)。

#三、换密码 / 换密钥的语义

  • SITE_PASSWORD(所有站改成同一新值)→ 换登录密码。
  • AUTH_SECRET → 让所有人当前登录立即失效(相当于强制全体登出)。

#四、独立门 vs 共享门

  • 想把某个站单独发密码给某一个人用时,就给它一套独立的密码门:Cookie 只挂本域(而非父域),用独立的 SITE_PASSWORD / AUTH_SECRET。这样共享门的密码进不来、这把密码也进不了别的站,隔离清晰。

#五、静态站为什么加不了这种门

  • 若 DNS 托管在 Vercel 免费版(vercel-dns),免费版无法给静态站的自定义域名加密码保护。
  • 要锁静态站,两条路:把 DNS 迁到 Cloudflare 用 Cloudflare Access,或升级到 Vercel Pro 用原生 Password Protection。
  • 因此实践中静态站维持公开(确保其中无敏感信息),只有动态站加门。

#六、最重要的教训:sensitive 环境变量必须本地留档

  • 部署平台(Vercel)上标为 sensitive 类型的变量,写进去后 CLI、网页后台、REST API(即使带 ?decrypt=true)三条路都读不回明文
  • 曾发生:某条写类命令(vercel blob create-store)覆写了本地 .env.local,几个 sensitive 值当场变成孤本丢失,只有靠浏览器密码管理器存的记录才找回一个。
  • 铁律
    1. 跑任何 vercel 写类命令前先 cp .env.local .env.local.bak
    2. 凡是自己生成的密钥/密码,先在本地/离线文档留一份,再写到平台上。

来源:配置信息/站点访问密码门.md(整理于 2026-08-18)