单点登录(SSO)完整技术实现方案

一、核心概念

单点登录 SSO(Single Sign-On) :用户在一个信任域下任意系统登录一次,访问域内其他业务系统无需重复输入账号密码。

两类场景区分

  1. 同域 SSO :所有服务共享一级域名(*.shturl.),如 shturl.cc/Xkb、shturl.cc/R2
  2. 跨域 SSO :多独立域名(a.com、b.com、c.com),需要独立认证中心服务

三大主流标准协议

  1. Session 共享(简易同域) :适合小型单体集群,不支持跨域
  2. OAuth2.0 + JWT(企业内部 / 第三方登录) :最通用,前后端分离首选
  3. SAML2.0(政企、跨组织对接) :浏览器重定向 XML 加密,传统政务系统多用
  4. OIDC(OpenID Connect) :基于 OAuth2 封装,标准化身份 SSO(主流云平台)

二、方案 1:同域单点登录

适用场景

全部业务子系统共用根域名 *.company.com,传统 SpringBoot/PHP 单体项目。

实现原理

HTTP Cookie 携带 SessionID,根域共享 Cookie;统一 Redis 存储 Session,所有服务读取同一份会话。

流程

  1. 用户访问系统 A,未登录跳转统一登录页 (登录服务 auth.company.com)
  2. 输入账号密码校验成功,服务端生成全局 Session 存入 Redis,写入根域 Cookie company.com
  3. 跳转回系统 A,A 读取根域 Cookie 拿到 SessionID,从 Redis 获取登录态
  4. 访问系统 B(shturl.pany.com),浏览器自动带上根域 Cookie,B 校验 Redis 会话直接放行

优缺点

✅ 实现简单、无额外 token 解析、兼容性好 ❌ 仅支持同域名;Redis 压力大;会话集中存储有单点故障风险

关键配置示例(Spring Session Redis)

yaml 复制代码
spring:
  session:
    store-type: redis
    cookie:
      domain: .company.com # 根域共享Cookie
      max-age: 86400

三、方案 2:JWT + 认证中心(跨域 SSO,最常用)

架构分层(标准 SSO 三组件)

  1. 认证中心 Auth Server:唯一登录入口,负责账号校验、签发 JWT 令牌
  2. 资源服务 Resource Server:各业务系统(A、B、C),校验 JWT 鉴权
  3. 客户端 Client:前端页面、App、小程序

两种令牌设计

  1. Access Token(短期) :JWT,有效期短(15~60min),接口鉴权使用
  2. Refresh Token(长期) :存在 Redis / 数据库,用于无感刷新 AccessToken,避免频繁登录

完整登录流程(跨域场景,OIDC 简化版)

  1. 用户访问业务系统 A,检测无登录态,302 重定向至 auth.com/login?redirect=A系统回调地址

  2. 用户在认证中心输入账号密码,后端校验

  3. 校验通过,生成:

    • JWT AccessToken(存储用户身份、权限,无状态)
    • RefreshToken(唯一字符串,绑定用户存入 Redis)
  4. 认证中心写顶级域 Cookie保存 RefreshToken,同时携带 AccessToken 跳转回 A 系统回调地址

  5. A 前端接收 AccessToken,存在 localStorage,后续所有接口请求 Header 携带 Authorization: Bearer xxx

  6. 访问业务系统 B:直接重定向 auth 中心,浏览器自动携带 RefreshToken Cookie,认证中心自动签发新 AccessToken,免登

JWT 结构

  • Header:加密算法(HS256/RSA256)
  • Payload:用户 ID、用户名、过期时间、权限、iss 签发者
  • Signature:密钥签名,防止篡改

跨域核心解决点

  1. 不同域名无法共享 Cookie → 依靠认证中心统一跳转传递令牌
  2. 前端接口跨域 → 服务配置 CORS 放行认证中心域名
  3. 防止 XSS 窃取 Token:AccessToken 放 localStorage,RefreshToken 设 HttpOnly Cookie

优缺点

✅ 支持跨域名、分布式无状态、扩展性强、前后端分离友好 ❌ JWT 无法主动失效(需 Redis 黑名单做登出拦截);token 过大传输成本高

登出逻辑

  1. 前端清空本地 AccessToken
  2. 请求认证中心登出接口
  3. 服务端删除 Redis 中该用户 RefreshToken
  4. 清除根域 HttpOnly Cookie
  5. 维护 JWT 黑名单,短期内拦截未过期旧 AccessToken

四、方案 3:OIDC 标准化 SSO(企业级标准化)

基于 OAuth2.0 封装,定义统一身份接口,兼容第三方登录(钉钉、企业微信、Azure AD)

核心流程

  1. 业务系统发起授权请求到 OIDC 认证服务器
  2. 用户登录,服务器返回 ID Token(JWT)+ Access Token
  3. 业务系统通过 ID Token 解析用户身份,或调用 /userinfo 接口获取完整用户信息

适用场景

企业统一身份平台、第三方应用接入、多租户系统


五、方案 4:SAML2.0(政企、跨组织 SSO)

架构

  • IDP:身份提供商(统一登录服务)
  • SP:业务服务系统

流程

  1. SP 生成认证请求,跳转 IDP
  2. IDP 校验用户身份,生成 XML 加密断言(SAML Response)
  3. POST 回调至 SP 接口,SP 解析 XML 校验签名完成登录

特点

基于 XML、非 REST、安全性高、多用于政府 / 银行,开发复杂度高


六、分布式高可用落地架构(生产标准)

css 复制代码
前端页面A/B/C → Nginx负载均衡
        ↓(未登录跳转)
统一认证中心集群(Auth Server) ← Redis集群(会话/RefreshToken/JWT黑名单)
        ↓(签发token回调)
业务资源服务集群 A服务、B服务、C服务
        ↓
统一用户中心(账号、角色、权限数据库)

生产关键优化点

  1. 认证中心集群:无状态部署,水平扩容
  2. Redis 集群:持久化会话、RefreshToken、JWT 失效黑名单
  3. RSA 非对称加密签发 JWT:私钥在认证中心,各业务服务仅持有公钥校验,密钥更安全
  4. HttpOnly + SameSite Cookie:防御 CSRF 攻击
  5. Token 过期阶梯设计:AccessToken 短时效降低泄露风险,RefreshToken 延长免登周期
  6. 统一权限中心:JWT 仅携带基础 ID,复杂权限实时从权限服务拉取

七、常见安全问题与解决方案

表格

风险 解决方案
CSRF 跨站请求伪造 RefreshToken Cookie 配置 SameSite=Strict、携带 csrf 随机参数
XSS 窃取 Token AccessToken 存入 localStorage,RefreshToken 开启 HttpOnly 禁止 JS 读取
JWT 篡改 使用 RSA 非对称签名,禁止明文密钥
Token 泄露长期可用 AccessToken 短期失效,登出加入黑名单
跨域劫持 CORS 严格限制可信域名,回调地址白名单校验
会话劫持 登录绑定设备指纹(UA+IP),换设备强制重登

八、不同场景选型总结

  1. 小型内部系统、同域名集群 → Session+Redis 共享 Cookie
  2. 中大型前后端分离、多业务跨域名 → OAuth2+JWT 认证中心 SSO
  3. 对接第三方企业身份(钉钉 / 飞书)、标准化平台 → OIDC
  4. 政务、银行、跨机构合作系统 → SAML2.0
相关推荐
一个有温度的技术博主2 小时前
凌晨三点的消息堆积:当 MQ 变成“停车场“
java·后端·场景
谢亮_vipxieliang2 小时前
Go WaitGroup与Once——并发同步的基石
开发语言·后端·golang
ITOM运维行者2 小时前
PHP性能监控怎么做?从响应时间到慢函数的6个关键指标
前端·javascript·后端
RobinDevNotes3 小时前
亲手量化大模型,Mac实测和NVIDIA指南
人工智能·后端
Bazingga3 小时前
从0到1搭一个Agent:Spring AI显式ReAct循环完整实战
后端
LEE3 小时前
前端转型全栈 05:SQL 与迁移,AI 写的 SQL 怎么安全上线
前端·后端·ai编程
站大爷IP3 小时前
Python的列表删除把我坑惨了,原来remove和pop的区别这么大
后端
montEvergreen3 小时前
RTMP 王国的“信笺百科全书”
后端·go
北冥you鱼3 小时前
Go 语言 Channel 机制详解:从原理到实战
开发语言·后端·golang