如何实现单点登录(SSO)

1. 概述

单点登录(Single Sign-On,简称 SSO)是一种认证机制,允许用户在多个应用系统中,只需登录一次就可以访问所有互信的系统资源。SSO 是企业级系统中常见的用户身份验证解决方案,能够提高用户体验,减少多次登录的麻烦。

2. 使用场景

  • 企业内多个系统统一登录:员工在公司不同的业务系统间切换时,只需登录一次。
  • 跨平台认证:如谷歌、Facebook 等第三方平台的统一身份验证。
  • 提高安全性和用户体验:通过 SSO 提供的集中管理和安全机制,简化身份验证流程。

3. SSO 的主要流程

  1. 用户访问受保护的系统资源(应用 A)
  2. 应用 A 检测用户未登录,重定向至认证服务器(SSO 服务器)
  3. 用户登录 SSO 服务器:SSO 服务器验证用户的身份凭证。
  4. 登录成功后生成 Token:SSO 服务器创建一个全局认证 Token。
  5. 返回 Token 并重定向到应用 A:用户携带 Token 返回应用 A。
  6. 应用 A 验证 Token:应用 A 通过 Token 询问 SSO 服务器,确认该 Token 的合法性。
  7. 应用 A 授予用户访问权限
  8. 当用户访问其他应用(应用 B)时:由于用户已在 SSO 服务器登录,SSO 服务器直接返回已验证的 Token,应用 B 授予访问权限,无需再次登录。

4. 实现 SSO 的常见方案

适用于域名相同的场景下,通常在内部网络或者子域下使用。

  • 同一主域的子系统共享 Cookie:SSO 服务器设置 Cookie(如 example.com),该 Cookie 对所有 *.example.com 子域都有效。
  • 过程
    1. 用户访问 app1.example.com,未登录,重定向至 sso.example.com 进行登录。
    2. 登录后,SSO 服务器设置主域 Cookie,有效范围为 .example.com
    3. 用户访问 app2.example.com,Cookie 自动携带,应用系统无需再次验证,用户直接通过身份验证。
4.2 基于 OAuth2.0 / OpenID Connect 的 SSO

适用于跨域或跨平台场景。OAuth2.0 和 OpenID Connect 是标准协议,许多第三方登录服务使用该机制。

  • OAuth2.0 流程

    1. 用户访问应用 A,应用 A 重定向至 OAuth2.0 授权服务器(SSO 服务器)。
    2. 用户登录 SSO 服务器,授权给应用 A。
    3. SSO 服务器返回授权码给应用 A。
    4. 应用 A 使用授权码换取访问令牌(Token)。
    5. 应用 A 使用 Token 访问受保护的资源,并获取用户身份信息。
  • 适用场景

    • 社交平台的第三方登录(如 Google、Facebook、GitHub 登录)。
    • 分布式、跨平台系统中的统一认证需求。
4.3 基于 JWT(JSON Web Token)的 SSO

JWT 是一种紧凑型的身份验证标准,常用于跨域认证场景。它通过将用户信息(如用户 ID 等)编码在 Token 中,使 Token 可以跨域传递。

  • 流程

    1. 用户登录 SSO 服务器,成功后生成一个包含用户信息的 JWT。
    2. 用户访问应用 A 时,携带 JWT,应用 A 使用该 JWT 验证用户身份。
    3. 该 JWT 可以在多个系统间传递,使得用户只需登录一次,就可以访问多个系统。
  • 优点

    • 无需依赖 Cookie,支持跨域。
    • JWT 是自包含的,用户信息直接包含在 Token 中,服务端可以通过校验 JWT 验证用户身份。
4.4 基于 CAS(Central Authentication Service)的 SSO

CAS 是一个开源的 SSO 协议,提供了强大的集成能力,适用于企业级应用。

  • 流程
    1. 用户访问应用系统时,应用检测用户未登录,跳转至 CAS 服务器进行认证。
    2. CAS 服务器认证通过后,生成一个认证票据,并返回给应用系统。
    3. 应用系统使用该票据向 CAS 服务器验证用户身份。
    4. 验证成功后,应用系统为用户创建会话。

5. 详细实现步骤:基于 JWT 的 SSO 示例

5.1 用户登录并生成 JWT

在 SSO 服务器中进行身份验证,成功后生成 JWT。可以使用如 jsonwebtoken 库来生成 JWT。

const jwt = require('jsonwebtoken');
 
// 用户登录成功后,生成 JWT
function login(req, res) {
  const user = { id: 1, username: 'user' }; // 用户信息
  const token = jwt.sign(user, 'secret_key', { expiresIn: '1h' }); // 生成 Token
  res.json({ token });
}
5.2 应用系统验证 JWT

在用户请求受保护资源时,应用系统从请求中获取 JWT,并进行验证。

const jwt = require('jsonwebtoken');
 
// 中间件验证 JWT
function verifyToken(req, res, next) {
  const token = req.headers['authorization']; // 从请求头中获取 Token
  if (!token) return res.status(403).send('Token is required');
 
  jwt.verify(token, 'secret_key', (err, decoded) => {
    if (err) return res.status(401).send('Invalid Token');
    
    req.user = decoded; // 将解码后的用户信息存入请求对象
    next();
  });
}
5.3 前端请求

前端在登录成功后,将 JWT 保存到浏览器的 localStoragesessionStorage,并在后续请求中携带该 Token。

// 示例前端代码
fetch('/api/protected', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('token')}` // 在请求头中添加 JWT
  }
})
  .then(response => response.json())
  .then(data => console.log(data));

6. 常见问题

  • Token 安全性:JWT 中包含用户信息,建议不要将敏感信息放入 Token 中,并使用 HTTPS 传输 Token 以避免中间人攻击。
  • Token 失效处理:可以设置 Token 过期时间,并在前端或后端进行失效处理,如用户长时间未操作后需要重新登录。
  • 跨域问题:如果不同系统之间存在跨域请求,需要在服务器端配置 CORS,以允许跨域访问。

7. 总结

单点登录(SSO)提供了用户在多系统中统一登录的体验,可以使用多种方案来实现,如基于 Cookie 的 SSO、OAuth2.0、JWT 或 CAS 等。基于 JWT 的 SSO 方案适用于现代的分布式、跨域应用,具有简洁、易扩展的优势。