在 React js 应用程序中使用 Keycloak 注销用户的问题

Posted

技术标签:

【中文标题】在 React js 应用程序中使用 Keycloak 注销用户的问题【英文标题】:Problem in log out user using Keycloack in React js app 【发布时间】:2021-10-10 02:20:48 【问题描述】:

我正在 React js 应用程序中使用 Keycloak 实现身份验证。

在这种情况下,我使用React Context 来管理全局状态是KeycloackContext 中的keycloackValueauthenticated

案例:

    当应用程序第一次运行时,应用程序会检查keycloackValueauthenticated。它们的初始状态是nullfalse。 如果keycloackValueauthenticatednullfalse,应用程序将初始化keycloak 登录页面(通过在KeycloackContext 中调用keycloak.init)。 如果keycloackValueauthenticated分别是not nulltrue(表示用户通过keycloak登录成功),则应用会渲染MyApp.js中的所有组件。 如果用户点击Header组件中的log out button,应用程序会将keycloackValueauthenticated设置为nullfalse,同时调用keycloak注销用户。之后调用keycloak登录页面。

问题:

我使用 keycloak 成功登录。然后app成功渲染了MyApp.js中的所有组件。

问题是当我点击Header.js中的log out button时,keycloackValueauthenticated被成功设置为nullfalse,keycloak也成功注销。然后应用自行刷新并渲染MyApp.js中的所有组件(应用没有调用keycloak登录页面)。

代码:

index.js

import React from "react";
import ReactDOM from "react-dom";
import MyApp from './my/App'
import  KeycloackContextProvider  from "./my/contexts/KeycloackContext"
 
ReactDOM.render(
    <KeycloackContextProvider>
        <MyApp/>
    </KeycloackContextProvider>,
    document.getElementById("root")
);

KeyclockContext.js

import React,  createContext, useState, useEffect  from 'react'

// KEYCLOACK
import Keycloak from 'keycloak-js'

const KeycloackContext = createContext()

const KeycloackContextProvider = (props) => 
    const [ keycloackValue, setKeycloackValue ] = useState(null)
    const [ authenticated, setAuthenticated ] = useState(false)

    const setKeycloack = () => 
        const keycloak = Keycloak(
            realm: process.env.REACT_APP_WULING_KEYCLOAK_REALM,
            url: process.env.REACT_APP_WULING_KEYCLOAK_URL,
            clientId: process.env.REACT_APP_WULING_KEYCLOAK_CLIENTID,
        )

        keycloak.init(
            onLoad: 'login-required', 
            checkLoginIframe: false,
        ).then(authenticated => 
            setKeycloackValue(keycloak)
            setAuthenticated(authenticated)
        )
    

    const logout = () => 
        setKeycloack(null)
        setAuthenticated(false)
        keycloackValue.logout()
    

    useEffect(() => 
        setKeycloack()
    , [])

    return (
        <KeycloackContext.Provider
            value=
                keycloackValue,
                authenticated,
                logout
            
        >
            props['children']
        </KeycloackContext.Provider>
    )


export  KeycloackContextProvider, KeycloackContext 

MyApp.js

import React,  useContext  from 'react'
import BrowserRouter as Router, Switch, Route, Redirect  from 'react-router-dom'

import Header from './components/Header/Header'
import Tab from './components/Tab/Tab'

// CONTEXTS
import  KeycloackContext  from './contexts/KeycloackContext'

import Device from './pages/Device/Device'
import Stock from './pages/Stock/Stock'

const MyApp = () => 
    const  keycloackValue, authenticated  = useContext(KeycloackContext)

    return (
        (keycloackValue && authenticated) &&
        <div className='app-root'>
            <Router>
                <Header/>
                <Tab/>

                <Redirect from='/' to='/device'/>
                <Switch>
                    <Route exact path='/device'>
                        <Device/>
                    </Route>
                    <Route exact path='/stock'>
                        <Stock/>
                    </Route>
                </Switch>
            </Router>
        </div>
    )


export default MyApp

Header.js

import React,  useContext  from 'react'

// CONTEXTS
import  KeycloackContext  from '../../contexts/KeycloackContext'

const Header = () => 
    const  logout  = useContext(KeycloackContext)

    return (
        <div className='logout-popup-root'>
            <p className='logout-popup-username'>Username</p>
            <p className='logout-popup-email'>username@gmail.com</p>
            <div className='logout-popup-divider'></div>
            <div 
                className='logout-popup-logout-button'
                onClick=() => logout()
            >
                Log Out
            </div>
        </div>
    )


export default Header

相关依赖:

"keycloak-js": "^15.0.0",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-router-dom": "5.1.2",
"react-scripts": "3.2.0",

注意:

    我昨天尝试在另一个应用程序中使用此代码。该应用程序很好(一切都按我的意愿运行)。我不知道为什么这次代码没有像以前的应用程序那样运行。 如果这个问题不够清楚,请告诉我。我会尽快更新。

更新:

    在之前的应用程序(昨天的应用程序)中,如果我点击注销按钮,应用程序会刷新一次(从 keycloak)并出现 keycloak 登录页面。 在这个应用程序(今天的应用程序)中,如果我单击注销按钮,应用程序将刷新三次(从keycloak),keycloak 识别用户已通过身份验证(keycloackValueauthenticatednot null 和@ 987654363@),keycloak登录页面没有出现。

【问题讨论】:

【参考方案1】:

经过几天的尝试和调试,我终于找到了解决方案。

只需评论或删除KeycloackContext.js 中的checkLoginIframe: false。它解决了注销问题。

Keyclock.js

import React,  createContext, useState, useEffect  from 'react'

// KEYCLOACK
import Keycloak from 'keycloak-js'

const KeycloackContext = createContext()

const KeycloackContextProvider = (props) => 
    const [ keycloackValue, setKeycloackValue ] = useState(null)
    const [ authenticated, setAuthenticated ] = useState(false)

    const setKeycloack = () => 
        const keycloak = Keycloak(
            realm: process.env.REACT_APP_WULING_KEYCLOAK_REALM,
            url: process.env.REACT_APP_WULING_KEYCLOAK_URL,
            clientId: process.env.REACT_APP_WULING_KEYCLOAK_CLIENTID,
        )

        keycloak.init(
            onLoad: 'login-required', 
            // checkLoginIframe: false, // remove checkLoginIframe here
        ).then(authenticated => 
            setKeycloackValue(keycloak)
            setAuthenticated(authenticated)
        )
    

    const logout = () => 
        setKeycloack(null)
        setAuthenticated(false)
        keycloackValue.logout()
    

    useEffect(() => 
        setKeycloack()
    , [])

    return (
        <KeycloackContext.Provider
            value=
                keycloackValue,
                authenticated,
                logout
            
        >
            props['children']
        </KeycloackContext.Provider>
    )


export  KeycloackContextProvider, KeycloackContext 

【讨论】:

以上是关于在 React js 应用程序中使用 Keycloak 注销用户的问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Immutable.js 中使用 React 的不可变助手

在 react.js 中使用 three.js 创建 WebXR 应用程序时,我不断看到“XRWebGLLayer”未定义 no-undef

使用 Node.js 后端在我的 React 应用程序中启用 CORS

如何修复 React.Children.only 期望在 react.js 中接收单个 React 元素子项?

在 react (Node.JS) 中使用繁重/耗时的函数

如何在 React.js 中使用 react-router-dom 保持 App() 级别状态不重置?