برنامه نویسی

Single Sign-On (SSO): یک راهنمای جامع با React و ExpressJS

Single Sign-On (SSO) یک مکانیسم احراز هویت است که به کاربران امکان می دهد یک بار وارد سیستم شوند و به چندین برنامه یا سیستم متصل بدون نیاز به احراز هویت مجدد برای هر یک دسترسی داشته باشند. SSO احراز هویت کاربر را در یک سیستم واحد و قابل اعتماد متمرکز می‌کند (که اغلب به آن Identity Provider یا IdP می‌گویند)، که سپس اعتبارنامه‌ها را مدیریت می‌کند و نشانه‌ها یا داده‌های جلسه را برای تأیید هویت کاربر در سایر سرویس‌ها (معروف به ارائه‌دهندگان خدمات یا SP) صادر می‌کند.

در این راهنما، نحوه کار SSO، مزایا و معایب آن، موارد استفاده متداول و نمونه‌هایی از پیاده‌سازی SSO در یک API (Node.js با Express)، یک برنامه اصلی (React) و یک برنامه خارجی (React) را بررسی خواهیم کرد. ). با درک اصول و شیوه های SSO، سازمان ها می توانند تجربه کاربر، امنیت و کارایی عملیاتی را در برنامه ها و سیستم های خود افزایش دهند.

فهرست مطالب

پیوندها

ویدئوی نمایشی

ویدیو را تماشا کنید

ورود به سیستم (SSO)

Single Sign-On (SSO) یک مکانیسم احراز هویت است که به کاربران امکان می دهد یک بار وارد سیستم شوند و بدون نیاز به احراز هویت مجدد برای هر یک به چندین برنامه یا سیستم متصل دسترسی پیدا کنند.

SSO احراز هویت کاربر را در یک سیستم واحد و قابل اعتماد متمرکز می‌کند (که اغلب به آن Identity Provider یا IdP می‌گویند)، که سپس اعتبارنامه‌ها را مدیریت می‌کند و نشانه‌ها یا داده‌های جلسه را برای تأیید هویت کاربر در سایر سرویس‌ها (معروف به ارائه‌دهندگان خدمات یا SP) صادر می‌کند.

SSO چگونه کار می کند؟

SSO از طریق مکانیسم های مبتنی بر توکن ایمن مانند OAuth 2.0، OpenID Connect (OIDC)، یا Security Assertion Markup Language (SAML) عمل می کند. در اینجا یک جریان ساده شده است:

  • User Login: کاربر اعتبار خود را در Identity Provider (IdP) وارد می کند.

  • Token Issuance: IdP اعتبارنامه ها را تأیید می کند و یک رمز احراز هویت صادر می کند (به عنوان مثال، ادعای JWT یا SAML).

  • Service Access: رمز به ارائه‌دهندگان خدمات ارسال می‌شود که آن را تأیید می‌کنند و بدون نیاز به ورود بیشتر به آن دسترسی می‌دهند.

مزایای SSO

  • تجربه کاربری پیشرفته: کاربران می توانند با یک ورود به سیستم به چندین سرویس دسترسی داشته باشند که باعث کاهش اصطکاک و بهبود قابلیت استفاده می شود.

  • امنیت بهبود یافته:

    • خستگی رمز عبور را کاهش می دهد، که می تواند منجر به اقدامات ناامن مانند استفاده مجدد از رمز عبور شود.
    • احراز هویت متمرکز به سیاست های رمز عبور قوی تر و اجرای احراز هویت چند عاملی (MFA) اجازه می دهد.
  • مدیریت کاربر ساده شده:

    • مدیریت دسترسی کاربران در سراسر برنامه های متصل برای مدیران آسان تر است.
    • لغو دسترسی کاربر از IdP، دسترسی آنها را به تمام سیستم های یکپارچه غیرفعال می کند.
  • کارایی زمان و هزینه:

    • با کاهش درخواست های Help Desk مربوط به ورود به سیستم، در زمان برای کاربران و تیم های پشتیبانی صرفه جویی می کند.
    • با استفاده از مکانیزم های احراز هویت موجود، زمان و هزینه های توسعه را کاهش می دهد.
  • انطباق و حسابرسی:

    • احراز هویت متمرکز و کنترل دسترسی، اجرای سیاست های امنیتی و ردیابی فعالیت کاربر را آسان تر می کند.

معایب SSO

موارد استفاده برای SSO

  • برنامه های کاربردی سازمانی:

    • کارمندان می توانند با یک بار ورود به ابزارها و خدمات داخلی مختلف دسترسی داشته باشند.
    • فرآیندهای سوار شدن و خارج شدن از هواپیما را ساده می کند.
  • خدمات ابری:

    • کاربران می توانند به طور یکپارچه بین برنامه های ابری بدون ورود مکرر جابجا شوند.
    • بهره وری و تجربه کاربری را افزایش می دهد.
  • پورتال های مشتریان:

    • تجربه ورود یکپارچه را برای مشتریان در خدمات مختلف فراهم می کند.
    • شخصی سازی و بازاریابی هدفمند را امکان پذیر می کند.
  • ادغام شریک:

    • دسترسی ایمن به منابع مشترک بین سازمان های شریک را تسهیل می کند.
    • همکاری و تبادل داده را ساده می کند.

نمونه های پیاده سازی SSO

1. API (Node.js با Express)

API به عنوان ارائه دهنده هویت (IdP) عمل می کند. کاربران را احراز هویت می کند و توکن های JWT را برای دسترسی صادر می کند.

در زیر یک تفکیک ساختاری از کد ارائه شده است که هدف هر بخش را برای دنبال کنندگان شما توضیح می دهد. این به عنوان یک مثال قوی از نحوه پیاده سازی عملکرد SSO در لایه API است.

راه اندازی و وابستگی ها

در این تنظیمات از بسته های زیر استفاده می شود:

  • بیان کنید: برای رسیدگی به درخواست های HTTP و مسیریابی.
  • jsonwebtoken: برای تولید و تأیید JWT ها.
  • cors: برای رسیدگی به درخواست های متقاطع از برنامه های مشتری مختلف.
  • @faker-js/faker: برای تولید داده های کاربر ساختگی و انجام کار.
  • تجزیه کننده کوکی: برای تجزیه کوکی های ارسال شده در درخواست ها.
  • dotenv: برای بارگیری ایمن متغیرهای محیط.

پیکربندی

  • dotenv برای مدیریت امن کلید مخفی استفاده می شود.
  • یک راز بازگشتی برای محیط های توسعه ارائه شده است.
dotenv.config();
const SECRET_KEY = process.env.SECRET_KEY || "secret";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

میان افزار

  • CORS اطمینان حاصل می کند که درخواست ها از مبداهای فرانت اند خاص (main و external-app) مجاز هستند.
  • cookieParser کوکی های ارسال شده توسط مشتریان را تجزیه می کند.
  • express.json اجازه تجزیه بدنه های درخواست JSON را می دهد.
app.use(
  cors({
    origin: ["http://localhost:5173", "http://localhost:5174"],
    credentials: true,
  })
);
app.use(express.json());
app.use(cookieParser());
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

احراز هویت کاربر و تولید توکن

داده های ساختگی کاربران و کارهای مرتبط با آنها را شبیه سازی می کند.

کاربران دارای نقش ها (مدیر یا کاربر) و اطلاعات اولیه پروفایل هستند.
Todo ها برای دسترسی شخصی به شناسه های کاربر پیوند داده می شوند.

  • /login: احراز هویت کاربران بر اساس ایمیل و رمز عبور.

کاربران پس از ورود موفقیت آمیز یک کوکی (sso_token) حاوی JWT دریافت می کنند.
این توکن ایمن، فقط 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 را پاک می کند.

اطمینان حاصل می کند که کاربران می توانند با پاک کردن توکن خود به طور ایمن از سیستم خارج شوند.

app.post("/logout", (req, res) => {
  res.clearCookie("sso_token");
  res.json({ message: "Logout successful" });
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • /todos: کارهای مرتبط با کاربر احراز هویت شده را بازیابی می کند.
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/:id: یک کار را بر اساس شناسه ارائه شده به روز می کند.
// 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" });
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • /todos/:id: یک کار را بر اساس شناسه ارائه شده حذف می کند.
// 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" });
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2. برنامه اصلی (React)

برنامه اصلی به عنوان یک ارائه دهنده خدمات (SP) عمل می کند که API را مصرف می کند و تعاملات کاربر را مدیریت می کند.

در زیر یک تفکیک ساختاری از کد ارائه شده است که هدف هر بخش را برای دنبال کنندگان شما توضیح می دهد. این به عنوان یک مثال قوی از نحوه پیاده سازی عملکرد SSO در لایه برنامه اصلی عمل می کند.

مؤلفه App، احراز هویت کاربر و تغییر مسیرها را بر اساس وضعیت ورود به سیستم مدیریت می‌کند.

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="https://dev.to/" element={<Login />} />
        <Route
          path="/todos"
          element={isLoggedIn ? <Todos /> : <Navigate to={"/"} />}
        />
      Routes>
    div>
  );
}

export default App;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مولفه Login ورود کاربر را کنترل می کند و پس از احراز هویت موفقیت آمیز به صفحه Todos هدایت می شود.

import React, { useState } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import api from "../api";

function Login() {
  const [email, setEmail] = useState("admin@gmail.com");
  const [password, setPassword] = useState("admin");
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    const returnUrl = searchParams.get("returnUrl");
    setLoading(true);
    try {
      await api.post("/login", { email, password }, { withCredentials: true });
      toast.success("Login successful!");
      if (returnUrl) {
        window.location.href = returnUrl;
      } else {
        navigate("/todos");
      }
    } catch (error) {
      toast.error("Login failed. Please check your credentials.");
      console.error("Login failed:", error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="max-w-md mx-auto">
      <ToastContainer />
      <h2 className="mb-4 text-2xl font-bold">Loginh2>
      <form onSubmit={handleLogin} className="space-y-4">
        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full p-2 border border-gray-300 rounded"
        />
        <input
          type="password"
          placeholder="Password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full p-2 border border-gray-300 rounded"
        />
        <button
          type="submit"
          className="w-full p-2 text-white bg-blue-500 rounded"
          disabled={loading}
        >
          {loading ? "Logging in..." : "Login"}
        button>
      form>
    div>
  );
}

export default Login;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مؤلفه Todos کارهای مربوط به کاربر را نمایش می دهد و اجازه می دهد تا کارها را اضافه و حذف کنید.

import { useState, useEffect } from "react";
import { toast } from "react-toastify";
import { useNavigate } from "react-router-dom";
import api from "../api";

interface Todo {
  id: string;
  userId: string;
  title: string;
  description: string;
}

interface UserInfo {
  username: string;
  email: string;
  iat: number;
  exp: number;
}

function Todos() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const [loading, setLoading] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    fetchUserInfo();
    fetchTodos();

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

    document.addEventListener("visibilitychange", handleVisibilityChange);

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

  const fetchUserInfo = async () => {
    try {
      const response = await api.get("/verify", {
        withCredentials: true,
      });

      setUserInfo({
        username: response.data.user.user.username,
        email: response.data.user.user.email,
        iat: response.data.user.iat,
        exp: response.data.user.exp,
      });
    } catch (error) {
      toast.error("Error fetching user information.");
      console.error("Error fetching user information:", error);
    }
  };

  const fetchTodos = async () => {
    try {
      const response = await api.get("/todos", {
        withCredentials: true,
      });
      setTodos(response.data);
    } catch (error) {
      toast.error("Error fetching todos.");
      console.error("Error fetching todos:", error);
    }
  };

  const addTodo = async () => {
    if (!title || !description) {
      toast.error("Title and description are required.");
      return;
    }
    setLoading(true);
    try {
      const response = await api.post(
        "/todos",
        { title, description },
        { withCredentials: true }
      );
      setTodos([...todos, response.data.data]);
      setTitle("");
      setDescription("");
      toast.success("Todo added successfully!");
    } catch (error) {
      toast.error("Error adding todo.");
      console.error("Error adding todo:", error);
    } finally {
      setLoading(false);
    }
  };

  const deleteTodo = async (id: string) => {
    setLoading(true);
    try {
      await api.delete(`/todos/${id}`, {
        withCredentials: true,
      });
      setTodos(todos.filter((todo) => todo.id !== id));
      toast.success("Todo deleted successfully!");
    } catch (error) {
      toast.error("Error deleting todo.");
      console.error("Error deleting todo:", error);
    } finally {
      setLoading(false);
    }
  };

  const handleLogout = async () => {
    try {
      await api.post("/logout", {}, { withCredentials: true });
      toast.success("Logged out successfully!");
      navigate("/");
    } catch (error) {
      toast.error("Error logging out.");
      console.error("Error logging out:", error);
    }
  };

  return (
    <div>
      <header className="flex items-center justify-between">
        <h2 className="mb-4 text-2xl font-bold">Todosh2>
        <button
          onClick={handleLogout}
          className="p-2 mb-4 text-white bg-red-500 rounded-full"
        >
          Logout
        button>
      header>
      {userInfo && (
        <div>
          <p>Username: {userInfo.username}p>
          <p>Email: {userInfo.email}p>
        div>
      )}
      <form className="mb-4">
        <input
          type="text"
          placeholder="Title"
          value={title}
          required
          onChange={(e) => setTitle(e.target.value)}
          className="w-full p-2 mb-2 border border-gray-300 rounded"
        />
        <input
          type="text"
          placeholder="Description"
          value={description}
          required
          onChange={(e) => setDescription(e.target.value)}
          className="w-full p-2 mb-2 border border-gray-300 rounded"
        />
        <button
          onClick={addTodo}
          className="w-full p-2 text-white bg-green-500 rounded"
          disabled={loading}
        >
          {loading ? "Adding..." : "Add Todo"}
        button>
      form>
      <ul>
        {todos.map((todo) => (
          <li
            key={todo.id}
            className="flex items-center justify-between p-2 border-b"
          >
            <div>
              <h3 className="font-bold">{todo.title}h3>
              <p>{todo.description}p>
            div>
            <button
              onClick={() => deleteTodo(todo.id)}
              className="p-1 text-white bg-red-500 rounded"
              disabled={loading}
            >
              Delete
            button>
          li>
        ))}
      ul>
    div>
  );
}

export default Todos;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

3. برنامه خارجی (React)

برنامه خارجی به عنوان یک ارائه دهنده خدمات دیگر (SP) عمل می کند که API را مصرف می کند و تعاملات کاربر را مدیریت می کند.

در زیر یک تفکیک ساختاری از کد ارائه شده است که هدف هر بخش را برای دنبال کنندگان شما توضیح می دهد. این به عنوان یک مثال قوی از نحوه اجرای عملکرد SSO در لایه برنامه خارجی است.

مؤلفه App، احراز هویت کاربر و تغییر مسیرها را بر اساس وضعیت ورود به سیستم مدیریت می‌کند.

import { useState, useEffect } from "react";
import Todos from "./components/Todos";
import api from "./api";

const MAIN_APP_URL = import.meta.env.VITE_MAIN_APP_URL;

function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  useEffect(() => {
    const verifyLogin = async () => {
      try {
        const response = await api.get("/verify", {
          withCredentials: true,
        });
        if (response.data.authenticated) {
          setIsLoggedIn(true);
        } else {
          setIsLoggedIn(false);
        }
      } catch (error) {
        console.error("Verification failed:", error);
        setIsLoggedIn(false);
      }
    };

    verifyLogin();

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

    document.addEventListener("visibilitychange", handleVisibilityChange);

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

  return (
    <div className="container p-4 mx-auto">
      {isLoggedIn ? (
        <Todos />
      ) : (
        <div className="text-center">
          <h2 className="mb-4 text-2xl font-bold">You are not logged inh2>
          <a
            href={`${MAIN_APP_URL}?returnUrl=${window.location.href}`}
            target="_blank"
            rel="noopener noreferrer"
            className="p-2 text-white bg-blue-500 rounded"
          >
            Go to Main App to Login
          a>
        div>
      )}
    div>
  );
}

export default App;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مؤلفه Todos کارهای مربوط به کاربر را نمایش می دهد.

import { useState, useEffect } from "react";
import { toast } from "react-toastify";
import api from "../api";

interface Todo {
  id: string;
  userId: string;
  title: string;
  description: string;
}

function Todos() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [title, setTitle] = useState("");
  const [description, setDescription] = useState("");
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    fetchTodos();

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

    document.addEventListener("visibilitychange", handleVisibilityChange);

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

  const fetchTodos = async () => {
    try {
      const response = await api.get("/todos", {
        withCredentials: true,
      });
      setTodos(response.data);
    } catch (error) {
      toast.error("Error fetching todos.");
      console.error("Error fetching todos:", error);
    }
  };

  const addTodo = async () => {
    if (!title || !description) {
      toast.error("Title and description are required.");
      return;
    }
    setLoading(true);
    try {
      const response = await api.post(
        "/todos",
        { title, description },
        { withCredentials: true }
      );
      setTodos([...todos, response.data.data]);
      setTitle("");
      setDescription("");
      toast.success("Todo added successfully!");
    } catch (error) {
      toast.error("Error adding todo.");
      console.error("Error adding todo:", error);
    } finally {
      setLoading(false);
    }
  };

  const deleteTodo = async (id: string) => {
    setLoading(true);
    try {
      await api.delete(`/todos/${id}`, {
        withCredentials: true,
      });
      setTodos(todos.filter((todo) => todo.id !== id));
      toast.success("Todo deleted successfully!");
    } catch (error) {
      toast.error("Error deleting todo.");
      console.error("Error deleting todo:", error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <h2 className="mb-4 text-2xl font-bold">External App - Todosh2>
      <form className="mb-4">
        <input
          type="text"
          placeholder="Title"
          value={title}
          required
          onChange={(e) => setTitle(e.target.value)}
          className="w-full p-2 mb-2 border border-gray-300 rounded"
        />
        <input
          type="text"
          placeholder="Description"
          value={description}
          required
          onChange={(e) => setDescription(e.target.value)}
          className="w-full p-2 mb-2 border border-gray-300 rounded"
        />
        <button
          onClick={addTodo}
          className="w-full p-2 text-white bg-green-500 rounded"
          disabled={loading}
        >
          {loading ? "Adding..." : "Add Todo"}
        button>
      form>
      <ul>
        {todos.map((todo) => (
          <li
            key={todo.id}
            className="flex items-center justify-between p-2 border-b"
          >
            <div>
              <h3 className="font-bold">{todo.title}h3>
              <p>{todo.description}p>
            div>
            <button
              onClick={() => deleteTodo(todo.id)}
              className="p-1 text-white bg-red-500 rounded"
              disabled={loading}
            >
              Delete
            button>
          li>
        ))}
      ul>
    div>
  );
}

export default Todos;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه گیری

Single Sign-On (SSO) احراز هویت کاربر و مدیریت دسترسی در چندین برنامه را ساده می کند و تجربه کاربر، امنیت و کارایی عملیاتی را افزایش می دهد. با متمرکز کردن احراز هویت و استفاده از مکانیسم‌های مبتنی بر توکن ایمن، سازمان‌ها می‌توانند دسترسی کاربر را ساده‌سازی کنند، خطرات مربوط به رمز عبور را کاهش دهند و قابلیت‌های انطباق و ممیزی را بهبود بخشند.

در حالی که SSO مزایای بی شماری را ارائه می دهد، چالش هایی مانند نقاط شکست واحد، الزامات پیاده سازی پیچیده، خطرات امنیتی و قفل شدن فروشنده بالقوه را نیز به همراه دارد. سازمان‌ها باید راه‌حل‌های SSO را برای کاهش این خطرات و به حداکثر رساندن مزایای احراز هویت متمرکز به دقت برنامه‌ریزی و اجرا کنند.

با پیروی از بهترین شیوه‌ها، استفاده از پروتکل‌های ایجاد شده و انتخاب استانداردهای باز، سازمان‌ها می‌توانند با موفقیت SSO را برای افزایش تجربه کاربر، امنیت و کارایی عملیاتی در برنامه‌ها و سیستم‌های خود پیاده‌سازی کنند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا