首頁 >web前端 >js教程 >單一登入 (SSO):React 和 ExpressJS 綜合指南

單一登入 (SSO):React 和 ExpressJS 綜合指南

Susan Sarandon
Susan Sarandon原創
2025-01-06 01:17:07872瀏覽

單一登入 (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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn