8.0 KiB
8.0 KiB
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— 登录,种 CookiePOST /api/logout— 登出,清 CookieGET /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
2.3 跨域 Cookie 可行性分析
| 条件 | 值 | 结论 |
|---|---|---|
| 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 接入不改变此现状;后续可考虑升级为后端存储 |