Files
nanocode/www/docs/sso-integration-plan.md
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

8.0 KiB
Raw Permalink Blame History

WWW 门户 SSO 接入方案

1. 现状分析

1.1 www 项目当前架构

  • 纯前端 SPA,单文件 index.html,由 nginx 80 端口静态托管
  • 无后端,所有逻辑(认证、数据增删)均在客户端 JS 完成
  • 当前认证方式:
    • 硬编码账号 admin / abcd1234
    • 密码经 SHA-256 哈希后存入 localStorage
    • 登录态由 localStorage.auth 标记
  • 已有功能:登录、修改密码、添加链接、删除链接

1.2 SSO 中心(usercenter)现状

  • FastAPI 服务,运行在 :9000
  • Cookie 配置:Domain=129.211.173.87,SameSite=Lax,HttpOnly=True
  • 提供的 API:
    • GET / — 登录页(支持 ?redirect= 参数)
    • POST /api/login — 登录,种 Cookie
    • POST /api/logout — 登出,清 Cookie
    • GET /api/userinfo — 返回 {"username":"admin"}(需 Cookie 认证)
    • POST /api/change-password — 修改密码
  • CORS 当前仅允许 :8888、:8889、:9000

1.3 关键差异

项目 personal-blog www
后端 FastAPI (:8889) 无后端
认证方式 Bearer Token + SSO Cookie 双模式 纯 localStorage
接入难度 中(需改后端认证逻辑) 低(仅改前端 JS)

2. 接入方案

2.1 核心思路

www 项目没有后端,无法做服务端 Cookie 验签。采用 前端 API 探测 方案:

  • 页面加载时,JS 跨域调用 SSO 中心 /api/userinfo(携带 Cookie)
  • 返回 200 → 已登录 → 展示管理功能
  • 返回 401 → 未登录 → 展示 SSO 登录按钮
  • 登录按钮跳转 SSO 中心,登录后 redirect 回门户

2.2 认证流程

用户访问 http://129.211.173.87/ (门户首页)
        │
        ▼
  JS 调用 SSO /api/userinfo(credentials: 'include')
        │
   ┌────┴────┐
   │         │
 200 OK    401
   │         │
   ▼         ▼
 展示     展示
 管理UI   「SSO 登录」按钮
           │
           ▼ 点击
     跳转 http://129.211.173.87:9000/?redirect=http://129.211.173.87/
           │
           ▼ SSO 登录成功
     浏览器种 Cookie → redirect 回门户
           │
           ▼
     页面重新加载 → JS 再调 /api/userinfo → 200 → 展示管理UI
条件 值 结论
SSO Cookie Domain 129.211.173.87 ✅ www (:80) 同 IP,浏览器会带 Cookie
SameSite Lax ✅ 顶级导航跳转(redirect)带 Cookie;跨域 fetch 需 credentials: 'include'
CORS allow_credentials True ✅ SSO 中心已开启
CORS allow_origins 需追加 http://129.211.173.87 ⚠️ 需修改

注意:SameSite=Lax 下,跨域 fetch + credentials: 'include' 在部分浏览器版本中行为不一致。但当前场景中,用户从 SSO 中心 redirect 回门户时是顶级导航,Cookie 一定会带上。fetch 探测仅用于页面加载后的状态检查,即使偶尔探测失败,刷新页面即可恢复。

3. 具体改动

3.1 SSO 中心(usercenter)— 1 处改动

文件:/home/ubuntu/nanocode/usercenter/app.py

# CORS allow_origins 新增一行
allow_origins=[
    "http://129.211.173.87:8888",
    "http://129.211.173.87:8889",
    "http://129.211.173.87:9000",
    "http://129.211.173.87",         # ← 新增:www 门户(80 端口)
],

3.2 www 门户 — 1 个文件改动

文件:/home/ubuntu/nanocode/www/index.html

改动清单

# 改动 说明
1 删除 hashPass / checkPass / genId 等本地认证函数 不再需要
2 删除 localStorage 中 auth 相关的读写 不再需要
3 isLoggedIn() 改为基于 SSO 状态变量 异步检测,非同步读 localStorage
4 新增 checkSSOStatus() 调用 http://129.211.173.87:9000/api/userinfo,credentials: 'include'
5 页面加载时调用 checkSSOStatus() 替代原 updateNavbar() 的同步逻辑
6 登录按钮改为跳转 SSO window.location.href = 'http://129.211.173.87:9000/?redirect=...'
7 删除「修改密码」入口 密码管理统一到 SSO 中心
8 退出改为调用 SSO /api/logout fetch('.../api/logout', {method:'POST', credentials:'include'})
9 删除登录弹窗 HTML 不再需要本地登录表单
10 删除修改密码弹窗 HTML 不再需要

核心代码示意

// SSO 配置
const SSO_BASE = 'http://129.211.173.87:9000';
let ssoUser = null; // 当前 SSO 登录用户名

// 检测 SSO 登录状态
async function checkSSOStatus() {
  try {
    const res = await fetch(`${SSO_BASE}/api/userinfo`, {credentials: 'include'});
    if (res.ok) {
      const data = await res.json();
      ssoUser = data.username;
    } else {
      ssoUser = null;
    }
  } catch {
    ssoUser = null;
  }
  updateNavbar();
}

// SSO 登录(跳转)
function ssoLogin() {
  const redirect = window.location.origin + window.location.pathname;
  window.location.href = `${SSO_BASE}/?redirect=${encodeURIComponent(redirect)}`;
}

// SSO 登出
async function ssoLogout() {
  await fetch(`${SSO_BASE}/api/logout`, {method: 'POST', credentials: 'include'});
  ssoUser = null;
  updateNavbar();
}

// 是否已登录(SSO)
function isLoggedIn() {
  return ssoUser !== null;
}

// 页面加载
checkSSOStatus();
renderCards();

导航栏 UI 变化

未登录时:

  • 按钮:「SSO 统一登录」(跳转 SSO 中心)

已登录时:

  • 显示用户名
  • 按钮:「添加链接」「退出」
  • 移除「修改密码」(统一在 SSO 用户中心管理)

4. 实施步骤

Step 1:修改 SSO 中心 CORS

# 编辑 /home/ubuntu/nanocode/usercenter/app.py
# allow_origins 增加 "http://129.211.173.87"
# 重启 usercenter 服务
sudo systemctl restart usercenter

Step 2:修改 www 门户 index.html

1. 删除:本地认证相关 JS 函数(hashPass、checkPass、genId、doLogin、doChangePass)
2. 删除:localStorage auth 读写函数(setAuth、clearAuth、getAuth)
3. 删除:登录弹窗 HTML(loginModal)
4. 删除:修改密码弹窗 HTML(passModal)
5. 新增:SSO 状态检测函数 checkSSOStatus()
6. 新增:ssoLogin()、ssoLogout() 函数
7. 修改:isLoggedIn() → 基于 ssoUser 变量
8. 修改:updateNavbar() → 未登录显示「SSO 统一登录」,已登录显示用户名 + 退出
9. 修改:页面初始化 → checkSSOStatus() 替代同步 updateNavbar()

Step 3:验证测试

# 1. 验证 SSO CORS 配置
curl -s -H "Origin: http://129.211.173.87" \
     -H "Cookie: access_token=<valid_token>" \
     http://129.211.173.87:9000/api/userinfo

# 2. 浏览器测试
# - 访问 http://129.211.173.87/ → 应显示未登录状态
# - 点击「SSO 统一登录」→ 跳转到 :9000 登录页
# - 输入 admin / abcd1234 → 登录成功 redirect 回门户
# - 门户应显示已登录状态,可添加/删除链接
# - 点击「退出」→ 回到未登录状态

Step 4:清理遗留

# 清除浏览器 localStorage 中旧的 auth 数据
# (无需手动处理,新版代码不再读取旧 auth key,用户清除浏览器缓存即可)

5. 不改动的部分

  • 链接数据(localStorage.links)— 保持不变,与认证无关
  • 卡片渲染/3D 倾斜 — 保持不变
  • nginx 配置 — 保持不变
  • SSO 中心核心逻辑 — 仅改 CORS,不动登录/验签/密码逻辑
  • personal-blog 项目 — 无需改动

6. 风险与注意事项

风险 应对
跨域 fetch 携带 Cookie 在部分浏览器可能失败 顶级导航(SSO redirect 回来)后页面刷新自动带 Cookie,fetch 仅作辅助检测
SSO 中心宕机导致门户无法判断登录态 checkSSOStatus() 捕获异常后按未登录处理,不影响门户浏览;管理功能暂不可用
localStorage.links 数据无服务端保护 原本就是纯前端存储,SSO 接入不改变此现状;后续可考虑升级为后端存储