将 OIDC 添加到具有受限路由的 React 应用程序

2024-07-04

我想将 OIDC 添加到我的 React 应用程序中,并且我正在使用oidc-客户端-ts https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx因为它似乎很受欢迎并且仍在维护中。我的问题是我错过了一些 React 示例。

我想要的是除了一条路线之外的所有路线都受到保护。如果用户未经过身份验证,则应将他们重定向到登录屏幕,其中有一个按钮可使用自定义提供程序激活身份验证流程。

我尝试过使用these https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx two https://github.com/authts/sample-angular-oidc-client-ts示例,但我不确定如何将它们粘合在一起以及如何将 Angular 代码转换为 React。

到目前为止,我已将整个应用程序包装在 AuthContext 中,并将除一条路由之外的所有路由设为私有,如第一个例子 https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx:

索引.tsx:

<StrictMode>
    <AuthProvider>
        <BrowserRouter>
            <Routes>
                <Route path={routes.LOGIN} element={<LoginContainer />} />
                <Route element={<Layout />}>
                    <Route index element={<Home />} />
                    <Route path="/openid/callback" element={<AuthCallback />} />
                        // Other pages
                </Route>
                <Route path="*" element={<ErrorPage />} />
            </Routes>
        </BrowserRouter>
    </AuthProvider>
</StrictMode>

The Layout- 具有私有路由的组件,以创建所有路径,但"/login"私人的:

function RequireAuth({ children }: { children: JSX.Element }) {
    const auth = useAuth();

    if (!auth.user) {
        return <Navigate to="/login" replace />;
    }

    return children;
}

function Layout() {
    return (
        <RequireAuth>
            <>
                <Header />
                <Main />
                <Footer />
            </>
        </RequireAuth>
    );
}

授权提供者:

const AuthContext = createContext<AuthContextType>(null!);

const useAuth = () => useContext(AuthContext);

function AuthProvider({ children }: { children: React.ReactNode }) {
    const [user, setUser] = useState<any>(null);
    const authService = new AuthService();

    const login = () => authService.login().then(user1 => setUser(user1));

    const loginCallback = async () => {
        const authedUser = await authService.loginCallback();
        setUser(authedUser);
    };

    const logout = () => authService.login().then(() => setUser(null));
    const value = { user, login, logout };

    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export { AuthProvider, useAuth };

The authService刚刚复制自角度示例 https://github.com/authts/sample-angular-oidc-client-ts/blob/main/src/app/core/services/auth.service.ts:

import { User, UserManager } from "oidc-client-ts";

export default class AuthService {
    userManager: UserManager;

    constructor() {
        const settings = {
            authority: "...",
            client_id: "...",
            redirect_uri: "http://localhost:3000/openid/callback",
            client_secret: "...",
            post_logout_redirect_uri: "http://localhost:3000/login"
        };
        this.userManager = new UserManager(settings);
    }

    public getUser(): Promise<User | null> {
        return this.userManager.getUser();
    }

    public login(): Promise<void> {
        return this.userManager.signinRedirect();
    }

    public loginCallback(): Promise<User> {
        return this.userManager.signinRedirectCallback();
    }

    public logout(): Promise<void> {
        return this.userManager.signoutRedirect();
    }
}

我的问题是我不知道如何在中设置用户AuthProvider这样我就可以检查我是否已在RequireAuth-成分。我的里面没有设置then in the AuthProviders 登录和注销功能,所以每当我尝试登录时,我都会被重定向到登录页面。

有人可以告诉我如何使用 OIDC 进行身份验证流程并限制所有路径,但仅允许经过身份验证的用户访问吗?

此外这个答案 https://stackoverflow.com/a/59370777/7925759说应该有一个AuthorizationCallback- 解析 URL 的组件。当我使用oidc-客户端-ts https://stackblitz.com/github/remix-run/react-router/tree/main/examples/auth?file=src%2FApp.tsx这似乎为我解析数据,我真的需要这个额外的步骤还是我可以将重定向 URL 设为“/”或“/home”?

Edit:

我发现signinRedirect转到新的 URL,这意味着脚本的其余部分永远不会运行。signinRedirectCallback是返回用户的调用。当我弄清楚如何正确保护路线时,我会将其作为答案发布。办理入住手续RequireAuth在设置用户之前完成。如何推迟检查,直到设置用户,这样即使我已登录也不会重定向到登录?如果我刷新页面,我就会丢失user状态来自AuthProvider即使有活动会话,我也会被发送到登录页面。当我以干净的方式加载应用程序时,我不确定在哪里以及如何检查是否有会话正在运行。


我从德鲁·里斯的回答中得到了灵感,成功地解决了这个问题。 事情是这样的oidc-客户端-ts https://github.com/authts/oidc-client-ts' signinRedirect将重定向到身份验证服务器,因此 React 代码将停止其执行,并且then块永远不会运行。诀窍是使用登录重定向回调 https://authts.github.io/oidc-client-ts/classes/UserManager.html#signinRedirectCallback which processes the response from the authorization endpoint登录调用后。所以当我点击重定向网址时(http://localhost:3000/openid/callback),我称之为signinRedirectCallback看看我是否应该去Home or Login-成分。所以所有路线,但/login并且登录后重定向 URL 将受到身份验证保护:

<Routes>
  <Route path={routes.LOGIN} element={<LoginContainer />} />
  <Route path="/openid/callback" element={<AuthCallback />} />
  <Route element={<LayoutWithAuth />}>
    <Route index element={<Home />} />
    ...
  </Route>
  <Route path="*" element={<ErrorPage />} />
</Routes>;

然后重定向回应用程序,loginCallback/signingRedirectCallback(第一个只是将呼叫转接到authService),将用户设置为AuthContext(见下文)然后我导航到主页:

验证回调:

function AuthCallback() {
  const auth = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    auth.loginCallback().then(() => {
      navigate(routes.INDEX);
    });
  }, []);

  return <div>Processing signin...</div>;
}

通过制作loginCallback异步,我确保当我重定向到登录页面时,用户将被设置LayoutWithAuth-component 进行身份验证检查:

function RequireAuth({ children }: { children: JSX.Element }) {
  const auth = useAuth();
  return auth.user === undefined ? <Navigate to="/login" replace /> : children;
}

function LayoutWithAuth() {
  return (
    <RequireAuth>
      <>
        <Header />
        <Body />
        <Footer />
      </>
    </RequireAuth>
  );
}

oidc-client-ts将用户保存在会话存储 https://developer.mozilla.org/pt-BR/docs/Web/API/Window/sessionStorage,所以如果刷新页面,AuthContext首先检查 sessionStorage 以查看用户是否经过身份验证:

验证上下文:

const AuthContext = createContext<AuthContextType>(null!);

const useAuth = () => useContext(AuthContext);

function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null | undefined>(
    JSON.parse(
      sessionStorage.getItem(process.env.REACT_APP_SESSION_ID!) || "null"
    ) || undefined
  );

  const authService = new AuthService();

  const loginCallback = async (): Promise<void> => {
    const authedUser = await authService.loginCallback();
    setUser(authedUser);
  };

  // Login and logout methods

  const value = { user, login, logout };

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export { AuthProvider, useAuth };

Update:添加LoginContainer:

function LoginContainer() {
  const auth = useAuth();

  const login = () => {
    auth.login();
  };
  return (
    <Container>
      <Headline>Some headline</Headline>
      <Button variant="contained" onClick={() => login()}>
        {texts.BUTTON_LOGIN}
      </Button>
    </Container>
  );
}

export default LoginContainer;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 OIDC 添加到具有受限路由的 React 应用程序 的相关文章

随机推荐

  • WPF 应用程序中 WebBrowser 控件的叠加?

    可以给我一个提示 如何在我的 WPF 应用程序的 WebBrowser 控件中显示 覆盖控件 我想将导航图形显示为叠加层 以便用户可以选择一个功能 并且导航控件在选择它后消失 目前对 WPF 还没有真正的经验 我不知道从哪里开始 任何提示或
  • JavaScript 阻止表单提交

    当我按下 JavaScript 对话框上的取消按钮时 我试图让我的表单不提交 我有这个代码 document ready function submit click function e e preventDefault var link
  • Linux - 在 CLI 中更改主机名

    我不知道如何搜索这个 这就是我要求它的原因 我所有的搜索都没有透露任何相关信息 我有一个 Fedora 18 服务器 如下所示 root dhcp 192 168 5 100 我想将其更改为 root server1 目前 这台机器设置为通
  • 在 Maven BOM(物料清单)中定义依赖范围是一个好习惯吗?

    我有一个pom xml像这样用作 BOM 物料清单 定义的依赖项之一是 test用于测试使用此 BOM 中的库的代码的工件 问题是 指定 test神器只是为了testBOM 本身的范围 还是应该将其留给 BOM 用户在其项目的 POM 中指
  • 如何刷新 WatchApp 复杂功能

    所以我正在尝试更新headerTextProvider 每一秒都有一个Timer scheduledTimer withTimeInterval 1 repeats true timer in 我想这不是正确的做法吗 MY STUFF HE
  • keycloak 中基于短信的 OTP 可能吗?

    我正在探索 keycloak 我想构建一个基于用户手机号码的应用程序 SMS OTP 应该向用户进行身份验证 我没有找到任何地方 可以通过扩展身份验证机制 检查身份验证 SPI 文档 https www keycloak org docs
  • 使用 Scipy 将字典从 Python 保存到 Matlab

    我发现将整齐生成的数据保存到 mat 文件中时遇到一些问题 我认为使用 Scipy 更简单 但似乎我弄错了 这是我要保存的数据的示例 out features array 5 00088905e 01 1 51847522e 01 4 93
  • 即使为 pandas 指定编码,编码也是错误的

    我有一个包含重音字符的 CSV 文件 我用PyCharm和Sublime打开时检查了编码 它是西方的 Windows 1252或ISO 8859 1 我从此 CSV 创建一个 pandas 数据框 然后修改它 并将其导出到 UTF 8 文本
  • 扩展 Ember RESTAdapter 以与 CouchDB 配合使用

    我基本上使用 CouchDB 来处理我的整个后端 使用 Ember 来处理基本上我的整个前端 并且我需要找到一种方法来使 json 数据在两者之间兼容 特别是关于 命名根 约定 这里是这ember json 期望 http emberjs
  • Selenium2 中的 FirefoxDriver 是否有经过验证的 mouseOver 解决方法?

    我在用着硒Java 2 0b3 我有这个代码 WebDriver driver new InternetExplorerDriver Selenium seleniumDriver new WebDriverBackedSelenium d
  • Windows:Apache Spark 历史服务器配置

    我想使用 Spark 的 History Server 来利用 Web UI 的日志记录机制 但我发现在 Windows 计算机上运行此代码有些困难 我做了以下事情 设置我的 Spark defaults conf 文件以反映 spark
  • 启动 R 会话时安装软件包

    我对 R 编程相当陌生 我正在尝试自定义我的 R 设置 以便当 R 会话启动时 会在开始时安装一些软件包 我知道我可以在 Rprofile site 文件中编写一个 First 函数 但是 在 First 函数中添加我的安装包代码后 该包并
  • 在字典中按值获取键

    假设我的所有值都是唯一且未排序的 如何对字典执行以下操作 key value 152 780 87 688 2165 15 我想拿到所有钥匙 我想找到值 688 的密钥 我想获得所有的值 最好没有循环 也不必在外部对象中维护键值关系 我有时
  • C# readonly 关键字在 VB.NET 中的等效项是什么?

    在 C 中 您可以这样做以使您的成员变量不可变 public readonly int y 5 VB NET 中等效的 readonly 关键字是什么 足够令人惊奇的是 它是ReadOnly http msdn microsoft com
  • 禁用故事板警告

    我避免在我的快速代码中收到警告 然而 当谈到故事板的要求时 这对我来说有点困难 所以现在我只想禁用 xcode 显示有关情节提要问题的警告 我尝试了以下方法但没有成功 禁用显示警告选项 正如您提到的选项Show warnings under
  • Solrj 和动态字段

    我有一个 solr 模式 其中包含不同类型的动态字段 例如 在 schema xml 中有
  • 无法使用 webpack 和 Laravel mix 加载 popper.js

    我在我的项目中使用 bootstrap 4 beta 和 Laravel 5 4 并使用 npm 和 laravel mix 加载我的 js 依赖项 到目前为止 一切都运行良好 除了当我尝试使用 booostrap js 方法时 它向我抛出
  • “文件编号”到底是什么?

    我正在使用一些之前从 VB6 转换而来的 C 代码 它执行大量文件 I O 我到处都看到这样的情况 fn VBNET FileSystem FreeFile 其次是VB NET 文件系统 FileOpen 一些文件 I O 然后VB NET
  • 内存模型中的词法作用域是什么样的?

    假设我们有一个函数 function foo var x 10 function bar var y 20 return x y return bar console log foo 这在内存模型中会是什么样子 到目前为止 这就是我想象的堆
  • 将 OIDC 添加到具有受限路由的 React 应用程序

    我想将 OIDC 添加到我的 React 应用程序中 并且我正在使用oidc 客户端 ts https stackblitz com github remix run react router tree main examples auth