多 Web 端子系统共享会话:原理与实践
- 在多 Web 端子系统架构中(例如:app.example.com、admin.example.com、shop.example.com),用户通常期望一次登录即可在所有系统间无感跳转。
- 实现这一目标的关键在于:
txt
1、共享用户身份凭证(Session/Token)
2、集中管理登录状态与过期机制
3、保证安全性与一致性
- 本文基于同主域名、非同主域名两种方案分析常见的解决方案。
同主域名共享方案:Cookie + Session 集中存储
原理
- 在同主域名(如 example.com)下,可以通过设置 Domain=.example.com 的 Cookie,让所有子域共享同一会话标识符(如 SESSIONID)。后端通过集中式会话存储(如 Redis)统一管理会话数据,实现登录态的共享与续期。
实现方案
1. Cookie 设置要点
Domain=.example.com
→ 允许子域共享;Path=/
→ 全路径有效;HttpOnly=true
→ 防止 JS 读取;Secure=true
→ 仅在 HTTPS 传输;SameSite=None
→ 跨子域请求时必须。
示例(Spring Boot):
Java
ResponseCookie cookie = ResponseCookie.from("SESSIONID", sessionId)
.domain(".example.com")
.path("/")
.httpOnly(true)
.secure(true)
.sameSite("None")
.maxAge(Duration.ofHours(2))
.build();
response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());
2. 集中式 Session 管理
- 所有后端服务共享一个 Redis 作为会话存储:
text
key: session:abc123
val: { userId: 1001, roles: [...], expiresAt: 173.. }
3. 单点登出机制
后端提供登出接口:
- 删除 Redis 中的会话数据;
- 如需广播登出,可通过消息队列(Kafka / Redis PubSub)通知其他子系统同步。
跨主域方案:SSO 认证中心(推荐)
原理
跨域时 Cookie 无法共享,需通过 统一认证中心(SSO)
管理登录态。认证中心保存用户主会话,并在各前端域请求登录时发放临时 Token(如 OAuth2 的授权码),由各前端后端交换 Token 并建立本域的会话。
标准模型
- 基于
OAuth2 + OpenID Connect (OIDC)
; - 支持授权码 + PKCE 流程;
- 认证中心维护主 Session;
- 各前端服务通过 Token Exchange 获取访问令牌;
- 支持单点登录 / 单点登出。
登录流程
- 用户访问
appA.com
→ 未登录 → 重定向至auth.example.com
; - 登录成功 → Auth Server 发放授权码
code
; appA.com
后端通过code
换取 Token;- 后端为
appA.com
设置本域 HttpOnly Cookie; - 用户访问
appB.net
时,Auth Server 检测已有主 Session,直接签发 Token(无感登录)。
核心代码(Java 伪示例):
java
String code = request.getParameter("code");
TokenResponse token = oauthClient.exchangeCodeForToken(code);
String sessionId = createLocalSession(token);
setCookie(response, "SESSIONID", sessionId, domain = "appB.net");
优势
- 用户无感登录,支持单点登出;
- Token 标准化,易扩展第三方登录;
- 权限管理集中统一。
安全要点
- 使用短期 Access Token + 长期 Refresh Token;
- Token 过期、撤销、黑名单机制完善。
临时场景:URL Token 传递(不推荐长期使用)
- 用于跨域跳转、邮件免登录、短链分享等临时需求。
实现示例
-
appA.com
生成短期 Token 并跳转:urlhttps://appB.net/accept?token=eyJhbGciOi...
-
appB.net
校验 Token → 生成本地 Session → 重定向去除参数。
伪代码:
js
app.get('/accept', async (req, res) => {
const token = req.query.token;
const payload = verifyOneTimeToken(token);
if (!payload) return res.status(401).send('invalid');
const sessionId = await createLocalSession(payload.userId);
res.cookie('SESSIONID', sessionId, { httpOnly: true, secure: true });
res.redirect('/');
});
注意事项
- Token 必须短期有效(30 秒--5 分钟);
- 仅服务端验证使用,前端不可存储;
- 校验后立刻失效(防重放);
- 避免通过 Referer 泄露。
选型建议
场景 | 推荐方案 | 特点 |
---|---|---|
同主域名 | Cookie + 集中式 Session(Redis) | 简单高效,稳定可靠 |
跨主域 | SSO(OAuth2/OIDC) | 标准方案,无感体验 |
临时跨域 | URL Token | 短期可用,风险高 |
结语
- 同主域名:首选
Cookie + Redis Session 共享
,简单高效; - 跨主域:推荐
SSO 统一认证中心(OAuth2/OIDC)
,支持扩展与审计; - 临时跳转:可使用
URL Token
,但需严格时效与安全控制。