首页 > web前端 > js教程 > 单点登录 (SSO):React 和 ExpressJS 综合指南

单点登录 (SSO):React 和 ExpressJS 综合指南

Susan Sarandon
发布: 2025-01-06 01:17:07
原创
861 人浏览过

单点登录 (SSO) 是一种身份验证机制,允许用户登录一次并访问多个连接的应用程序或系统,而无需对每个应用程序或系统进行重新身份验证。 SSO 将用户身份验证集中到单个受信任的系统(通常称为身份提供商或 IdP)中,然后该系统管理凭据并颁发令牌或会话数据以跨其他服务(称为服务提供商或 SP)验证用户的身份。 🎜>

在本指南中,我们将探讨 SSO 的工作原理、其优点和缺点、常见用例以及 API(带有 Express 的 Node.js)、主应用程序 (React) 和外部应用程序中 SSO 实现的示例应用程序(反应)。通过了解 SSO 的原理和实践,组织可以增强其应用程序和系统的用户体验、安全性和运营效率。

目录

    单点登录 (SSO)
    • SSO 如何工作?
    • SSO 的好处
    • SSO 的缺点
    • SSO 用例
    • SSO 实施示例
    • 1. API(带有 Express 的 Node.js)
    • 2.主要应用程序(React)
    • 3.外部应用程序(React)
  • 结论
链接

    GitHub 存储库
演示视频

Single Sign-On (SSO): A Comprehensive Guide with React and ExpressJS

单点登录 (SSO)

单点登录 (SSO) 是一种身份验证机制,允许用户登录一次即可访问多个连接的应用程序或系统,而无需对每个应用程序或系统进行重新身份验证。

SSO 将用户身份验证集中到单个受信任的系统(通常称为身份提供商或 IdP)中,然后该系统管理凭据并颁发令牌或会话数据,以跨其他服务(称为服务提供商或 SP)验证用户身份).

单点登录如何工作?

SSO 通过基于安全令牌的机制(例如 OAuth 2.0、OpenID Connect (OIDC) 或安全断言标记语言 (SAML))进行操作。这是一个简化的流程:

  • 用户登录:用户在身份提供商 (IdP) 中输入其凭据。

  • 令牌颁发:IdP 验证凭据并颁发身份验证令牌(例如 JWT 或 SAML 断言)。

  • 服务访问:令牌被传递给服务提供商,服务提供商对其进行验证并授予访问权限,而无需进一步登录。

单点登录的好处

  • 增强的用户体验:用户只需一次登录即可访问多项服务,减少摩擦并提高可用性。

  • 提高安全性

    • 减少密码疲劳,避免密码重复使用等不安全行为。
    • 集中式身份验证可实现更强大的密码策略并实施多重身份验证 (MFA)。
  • 简化的用户管理

    • 管理员可以更轻松地管理跨连接应用程序的用户访问。
    • 从 IdP 撤销对用户的访问权限会禁用他们对所有集成系统的访问权限。
  • 时间和成本效率

    • 通过减少与登录相关的帮助台请求,为用户和支持团队节省时间。
    • 通过利用现有的身份验证机制减少开发时间和成本。
  • 合规与审核

    • 集中式身份验证和访问控制使执行安全策略和跟踪用户活动变得更加容易。

单点登录的缺点

  • 单点故障

    • 如果 IdP 不可用或受到威胁,用户将无法访问任何连接的系统。
    • 缓解措施:使用冗余 IdP 并确保高可用性。
  • 复杂的实现

    • 集成 SSO 需要大量的规划和专业知识,尤其是在具有不同应用程序和协议的环境中。
    • 缓解措施:利用 OAuth 2.0 或 SAML 等既定协议以及强大的 SSO 库。
  • 安全风险

    • 如果攻击者获得对用户 SSO 凭据的访问权限,他们就有可能访问所有连接的系统。
    • 缓解措施:实施强大的 MFA 并监控可疑的登录活动。
  • 供应商锁定

    • 组织可能严重依赖特定的 IdP 供应商,这使得迁移充满挑战。
    • 缓解措施:选择开放标准并避免专有解决方案。
  • 代币管理挑战

    • 过期或被盗的令牌可能会中断访问或造成安全漏洞。
    • 缓解措施:实施令牌过期、刷新机制和安全令牌存储。

SSO 用例

  • 企业应用

    • 员工只需登录即可访问各种内部工具和服务。
    • 简化入职和离职流程。
  • 云服务

    • 用户可以在云应用之间无缝切换,无需重复登录。
    • 提高生产力和用户体验。
  • 客户门户

    • 为不同服务的客户提供统一的登录体验。
    • 实现个性化和有针对性的营销。
  • 合作伙伴集成

    • 促进对合作伙伴组织之间共享资源的安全访问。
    • 简化协作和数据交换。

SSO 实施示例

1. API(Node.js 和 Express)

API 充当身份提供商 (IdP)。它对用户进行身份验证并颁发 JWT 令牌进行访问。

下面是所提供代码的结构化细分,为您的关注者解释了每个部分的目的。这是如何在 API 层实现 SSO 功能的可靠示例。

设置和依赖关系

此设置中使用了以下软件包:

  • express:用于处理 HTTP 请求和路由。
  • jsonwebtoken:用于生成和验证 JWT。
  • cors:用于处理来自不同客户端应用程序的跨源请求。
  • @faker-js/faker:用于生成模拟用户和待办事项数据。
  • cookie-parser:用于解析请求中发送的cookie。
  • dotenv:用于安全地加载环境变量。
配置
  • dotenv 用于安全地管理密钥。
  • 为开发环境提供了后备秘密。
dotenv.config();
const SECRET_KEY = process.env.SECRET_KEY || "secret";
登录后复制
登录后复制
中间件
  • CORS 确保允许来自特定前端来源(主应用程序和外部应用程序)的请求。
  • cookieParser 解析客户端发送的 cookie。
  • express.json 允许解析 JSON 请求主体。
app.use(
  cors({
    origin: ["http://localhost:5173", "http://localhost:5174"],
    credentials: true,
  })
);
app.use(express.json());
app.use(cookieParser());
登录后复制
登录后复制

用户身份验证和令牌生成

模拟数据模拟用户及其关联的待办事项。

用户拥有角色(管理员或用户)和基本个人资料信息。
待办事项链接到用户 ID 以进行个性化访问。

  • /login:根据电子邮件和密码对用户进行身份验证。

用户成功登录后会收到包含 JWT 的 cookie (sso_token)。
该令牌是安全的、仅限 HTTP 且有时间限制以防止篡改。

app.post("/login", (req, res) => {
  const { email, password } = req.body;
  const user = users.find(
    (user) => user.email === email && user.password === password
  );

  if (user) {
    const token = jwt.sign({ user }, SECRET_KEY, { expiresIn: "1h" });
    res.cookie("sso_token", token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      maxAge: 3600000,
      sameSite: "strict",
    });
    res.json({ message: "Login successful" });
  } else {
    res.status(400).json({ error: "Invalid credentials" });
  }
});
登录后复制
登录后复制
  • /verify:通过解码令牌来验证用户的身份。无效令牌会导致未经授权的响应。
app.get("/verify", (req, res) => {
  const token = req.cookies.sso_token;

  if (!token) {
    return res.status(401).json({ authenticated: false });
  }

  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    res.json({ authenticated: true, user: decoded });
  } catch {
    res.status(401).json({ authenticated: false, error: "Invalid token" });
  }
});
登录后复制
登录后复制
  • /logout:清除包含 JWT 令牌的 cookie。

确保用户可以通过清除令牌来安全注销。

dotenv.config();
const SECRET_KEY = process.env.SECRET_KEY || "secret";
登录后复制
登录后复制
  • /todos:检索与经过身份验证的用户关联的待办事项。
app.use(
  cors({
    origin: ["http://localhost:5173", "http://localhost:5174"],
    credentials: true,
  })
);
app.use(express.json());
app.use(cookieParser());
登录后复制
登录后复制
  • /todos:为经过身份验证的用户添加新的待办事项。
app.post("/login", (req, res) => {
  const { email, password } = req.body;
  const user = users.find(
    (user) => user.email === email && user.password === password
  );

  if (user) {
    const token = jwt.sign({ user }, SECRET_KEY, { expiresIn: "1h" });
    res.cookie("sso_token", token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      maxAge: 3600000,
      sameSite: "strict",
    });
    res.json({ message: "Login successful" });
  } else {
    res.status(400).json({ error: "Invalid credentials" });
  }
});
登录后复制
登录后复制
  • /todos/:id:根据提供的 ID 更新待办事项。
app.get("/verify", (req, res) => {
  const token = req.cookies.sso_token;

  if (!token) {
    return res.status(401).json({ authenticated: false });
  }

  try {
    const decoded = jwt.verify(token, SECRET_KEY);
    res.json({ authenticated: true, user: decoded });
  } catch {
    res.status(401).json({ authenticated: false, error: "Invalid token" });
  }
});
登录后复制
登录后复制
  • /todos/:id:根据提供的 ID 删除待办事项。
app.post("/logout", (req, res) => {
  res.clearCookie("sso_token");
  res.json({ message: "Logout successful" });
});
登录后复制

2. 主要应用程序(React)

主应用程序充当服务提供商 (SP),使用 API 并管理用户交互。

下面是所提供代码的结构化细分,为您的关注者解释了每个部分的目的。这是如何在主应用程序层中实现 SSO 功能的可靠示例。

  • 应用程序组件

App 组件管理用户身份验证并根据登录状态进行重定向。

app.get("/todos/:userId", (req, res) => {
  const ssoToken = req.cookies.sso_token;
  const user = getUser(ssoToken);

  if (!user) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  const userTodos = todos.filter((todo) => todo.userId === user.id);
  res.json(userTodos);
});
登录后复制
  • 登录组件

登录组件处理用户登录并在身份验证成功后重定向到 Todos 页面。

app.post("/todos", (req, res) => {
  const ssoToken = req.cookies.sso_token;
  const user = getUser(ssoToken);

  if (!user) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  const { title, description } = req.body;
  const newTodo = {
    id: faker.string.uuid(),
    userId: user.id,
    title,
    description,
  };

  todos.push(newTodo);
  res.status(201).json({ message: "Todo added successfully", data: newTodo });
});
登录后复制
  • 待办事项组件

Todos 组件显示用户特定的待办事项并允许添加和删除待办事项。

// Update a todo
app.put("/todos/:id", (req, res) => {
  const ssotoken = req.cookies.sso_token;
  const user = getUser(ssotoken);
  if (!user) {
    return res.status(401).json({ message: "Unauthorized" });
  }

  const { id } = req.params;
  const { title, description } = req.body;
  const index = todos.findIndex((todo) => todo.id === id);

  if (index !== -1) {
    todos[index] = {
      ...todos[index],
      title,
      description,
    };
    res.json({
      message: "Todo updated successfully",
      data: todos[index],
    });
  } else {
    res.status(404).json({ message: "Todo not found" });
  }
});
登录后复制

3. 外部应用程序(React)

外部应用程序充当另一个服务提供商 (SP),使用 API 并管理用户交互。

下面是所提供代码的结构化细分,为您的关注者解释了每个部分的目的。这是如何在外部应用程序层实现 SSO 功能的可靠示例。

  • 应用程序组件

App 组件管理用户身份验证并根据登录状态进行重定向。

// Delete a todo
app.delete("/todos/:id", (req, res) => {
  const ssoToken = req.cookies.sso_token;
  const user = getUser(ssoToken);
  if (!user) {
    return res.status(401).json({ message: "Unauthorized" });
  }

  const { id } = req.params;
  const index = todos.findIndex((todo) => todo.id === id);

  if (index !== -1) {
    todos = todos.filter((todo) => todo.id !== id);
    res.json({ message: "Todo deleted successfully" });
  } else {
    res.status(404).json({ message: "Todo not found" });
  }
});
登录后复制
  • 待办事项组件

Todos 组件显示用户特定的待办事项。

import { useState, useEffect } from "react";
import {
  Navigate,
  Route,
  Routes,
  useNavigate,
  useSearchParams,
} from "react-router-dom";
import Todos from "./components/Todos";
import Login from "./components/Login";
import { toast } from "react-toastify";
import api from "./api";

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [searchParams] = useSearchParams();
  const navigate = useNavigate();

  useEffect(() => {
    const verifyLogin = async () => {
      const returnUrl = searchParams.get("returnUrl");
      try {
        const response = await api.get("/verify", {
          withCredentials: true,
        });
        if (response.data.authenticated) {
          setIsLoggedIn(true);
          toast.success("You are logged in.");
          navigate("/todos");
        } else {
          setIsLoggedIn(false);
          if (!returnUrl) {
            toast.error("You are not logged in.");
          }
        }
      } catch (error) {
        setIsLoggedIn(false);
        console.error("Verification failed:", error);
      }
    };

    verifyLogin();

    const handleVisibilityChange = () => {
      if (document.visibilityState === "visible") {
        verifyLogin();
      }
    };

    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => {
      document.removeEventListener("visibilitychange", handleVisibilityChange);
    };
  }, [navigate, searchParams]);

  return (
    <div className="container p-4 mx-auto">
      <Routes>
        <Route path="/" element={<Login />} />
        <Route
          path="/todos"
          element={isLoggedIn ? <Todos /> : <Navigate to={"/"} />}
        />
      </Routes>
    </div>
  );
}

export default App;
登录后复制

结论

单点登录 (SSO) 简化了跨多个应用程序的用户身份验证和访问管理,从而增强了用户体验、安全性和运营效率。通过集中身份验证并利用基于安全令牌的机制,组织可以简化用户访问、降低与密码相关的风险并提高合规性和审核能力。

虽然 SSO 提供了众多好处,但它也带来了挑战,例如单点故障、复杂的实施要求、安全风险和潜在的供应商锁定。组织必须仔细规划和实施 SSO 解决方案,以减轻这些风险并最大限度地发挥集中式身份验证的优势。

通过遵循最佳实践、利用既定协议并选择开放标准,组织可以成功实施 SSO,以增强其应用程序和系统的用户体验、安全性和运营效率。

以上是单点登录 (SSO):React 和 ExpressJS 综合指南的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板