我在React js应用程序中使用Keycloak实现身份验证。
我使用React上下文来管理全局状态,在这种情况下是KeycloackContext
中的keycloackValue
和authenticated
。
案例:
- 应用程序第一次运行时,应用程序检查
keycloackValue
和authenticated
。初始状态为null
和false
。 - 如果
keycloackValue
和authenticated
是null
和false
,应用程序将初始化keycloak登录页面(通过调用KeycloackContext
中的keycloak.init
)。 - 如果
keycloackValue
和authenticated
是not null
和true
(意味着用户通过keycloak成功登录),那么应用程序将渲染MyApp.js
中的所有组件。 - 如果用户点击
Header
组件中的log out button
,应用会将keycloackValue
和authenticated
设置为null
,false
也会调用keycloak注销用户。之后,调用keycloak登录页面。
问题:
我使用keycloak成功登录。然后,应用程序成功渲染了MyApp.js
中的所有组件。
问题是当我点击Header.js
中的log out button
时,keycloackValue
和authenticated
被成功设置为null
和false
, 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")
);
KeycloackContext.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登录页面。
- 在这个app(今天的app)中,如果我点击注销按钮,app会刷新三次(来自keycloak), keycloak识别用户已通过认证(
keycloackValue
和authenticated
分别是not null
和true
), keycloak登录页面没有出现。
经过几天的尝试和调试,我终于找到了解决方案。
只需注释或删除KeycloackContext.js
中的checkLoginIframe: false
。它修复了登出问题。
Keycloack.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 }