๋ณธ๋ด์ฉ์ ํ์์ " React&Python์ผ๋ก ์น๊ฐ๋ฐ(๋งํฌ์๋น์ค)" ์นดํ ๊ณ ๋ฆฌ ๊ฐ๋ฐ์์ค์ ๊ณ์ ์ถ๊ฐํ๋ ๋ฐฉ์์ผ๋ก ์งํํฉ๋๋ค.
( ์์ค ํด๋๋ ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ์ ์ฝ์ด๋ณด๊ณ , git ์์ค๋ฅผ ์ฐธ๊ณ ํ์๊ธฐ ๋ฐ๋๋๋ค. )
react-keycloak ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ useKeycloak Hook๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ํ์ด์ง ํธ์ถ
์ด์ ๊ธ์์ Keycloak ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ํธ์ถํ์ฌ ๋ก๊ทธ์ธ์ ํ๋๋ก ์ฒ๋ฆฌํ์๋ค.
์ด๋ฒ์๋ Keycloak ๋ก๊ทธ์ธ ํ์ด์ง๋ฅผ ํตํด ๋ก๊ทธ์ธํ token ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ Keycloak์ ์ ์ฅ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ํ์ธ ํด๋ณด์.
์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ ์ ๊ท ์์ค๋ฅผ ์ถ๊ฐํ๋ค.
์ ๊ท 1๊ฐ์ ๊ธฐ์กด ์์ค๋ฅผ ์์ ํ๊ณ react์ ์ ๊ท ํจํค์ง๋ฅผ ์ค์นํ ์์ ์ด๋ค.
1. userinfo.jsx (์ ๊ท)
2. menu.jsx (์์ )
3. ์ ๊ท ํจํค์ง ์ค์น : yarn add base-64
๋จผ์ JWT ์ ๋ํด์ ๊ฐ๋จํ ์ค๋ช ํ์.
๋ก๊ทธ์ธ์ ํ ํฐ ๊ด๋ จ๋ ์ธ์ฆ์ ๋๋ถ๋ถ ์ฌ์ฉํ๋ค.
๊ทธ ์ค์์๋ JWT๋ ์น ํ์ค์ ๋ฐ๋ฅด๊ณ ์์ผ๋ฉฐ, JSON ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ณด๋ฅผ ์ ๋ฌํฉ๋๋ค.
JWT์ ์ฅ์ ์ค ํ๋๋ก, ์น ํ์ค์ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์ ๋๋ถ๋ถ์ ์ธ์ด๊ฐ ์ด๋ฅผ ์ง์ํฉ๋๋ค.
JWT๋ ๊ฐ๊ฐ์ ๊ตฌ์ฑ์์๊ฐ ์ (.) ์ผ๋ก ๊ตฌ๋ถ์ด ๋์ด ์์ผ๋ฉฐ , ๊ตฌ์ฑ์์๋ HEADER(ํค๋).PAYLOAD(๋ด์ฉ).SIGNATURE(์๋ช ) ํํ๋ก ๊ตฌ์ฑ๋์ด ์๋ค.
JWT ๋์ฝ๋ฉ ํ์ธ ์ฌ์ดํธ : jwt.io
์๋ ์์ค์์๋ ์ถ๊ฐ์ ์ผ๋ก PAYLOAD ๋์ฝ๋ฉ๊น์ง ํฌํจํ๋ค.
โ userinfo.jsx ( ์์น: linkservice/client/src/component/userinfo.jsx )
---------------------------------------------------------------------------------------------------
์๋ ์์ค๋ฅผ ๋ฐ์ํ๋ค.
import { useKeycloak } from "@react-keycloak/web";
import base64 from "base-64";
import React from "react";
const UserInfo = () => {
const { keycloak } = useKeycloak();
let payload, decodingInfo;
const accessToken = keycloak.idToken;
if (accessToken) {
payload = accessToken.split('.')[1];
decodingInfo = base64.decode(payload);
}
return (
<div className="container">
<div >
<p>idToken ์ ๋ณด</p>
<textarea cols="100" rows="8" value={keycloak.idToken} readOnly>
</textarea>
<p>token ์ ๋ณด</p>
<textarea cols="100" rows="8" value={keycloak.token} readOnly>
</textarea>
<p>payload ์ ๋ณด</p>
<textarea cols="100" rows="8" value={decodingInfo} readOnly>
</textarea>
</div>
</div>
)
}
export default UserInfo
๊ฐ๋จํ ์ค๋ช ํ๋ค๋ฉด,
useKeycloak Hook๋ฅผ ํตํด keycloak ๊ฐ๋ณ ๋ณ์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ํ ๋นํ๊ณ , accessToken๊ฐ์ ๋ฐ์์จ๋ค.
import { useKeycloak } from "@react-keycloak/web";
//....
//....
//....
const { keycloak } = useKeycloak();
// keycloak๋ฅผ ํตํด ๋ก๊ทธ์ธ์ด ๋์์ ๊ฒฝ์ฐ ํ ํฐ๊ฐ์ ๋ฐ๊ฒ ๋๋ค.
//.....
const accessToken = keycloak.idToken;
accessToken๊ฐ์ด ์์ ๊ฒฝ์ฐ, ์ฆ ์ ์ ๋ก๊ทธ์ธ์ด ๋์์๋, JWT๊ตฌ์กฐ์ splitํจ์๋ฅผ ํตํด PAYLOAD๊ฐ์ ๊ฐ์ ธ์์
base64๋ก ๋ค์ decode๋ฅผ ํ๋ค.
if (accessToken) {
payload = accessToken.split('.')[1];
decodingInfo = base64.decode(payload);
}
โ menu.jsx ( ์์น: linkservice/client/src/component/menu.js )
---------------------------------------------------------------------------------------------------
์ด์ menu.jsx ์์ค๋ฅผ ์์๋ก ์์ ํ์ฌ ์ ๋ณด๋ฅผ ํ์ธํด๋ณด์.
import HomeIcon from '@mui/icons-material/Home';
import SettingsIcon from '@mui/icons-material/Settings';
import { useNavigate } from 'react-router-dom';
// useKeycloak import
import { useKeycloak } from "@react-keycloak/web";
import UserInfo from './userinfo';
export default function MenuIcon(props) {
const navigator = useNavigate()
// Using Object destructuring
const { keycloak } = useKeycloak();
const handleIcon = (e) => {
switch (e) {
case 'A' :
// keycloak์ ๋ก๊ทธ์ธ์ด ๋์ด ์์ง ์๋ค๋ฉด, keycloak ๋ก๊ทธ์ธ ํ์ด์ง ํธ์ถ
if (!keycloak.authenticated) {
alert("๋ก๊ทธ์ธ์ด ํ์ํ ์๋น์ค์
๋๋ค.")
// keycloak ๋ก๊ทธ์ธ ํ์ด์ง ํธ์ถ์ redirectUri๋ฅผ ๊ฐ์ธํ๊ฐ ๊ฐ๋ฅํ๋ค.
keycloak.redirectUri= window.location.href + '/manager'
// keycloak ๋ก๊ทธ์ธ ํ์ด์ง ํธ์ถ
keycloak.login();
}
return navigator('/manager')
case 'M' : return navigator('/')
default : return navigator('/')
}
}
return (
<>
<div className='menu-wrap'>
<div className='menu-box'>
<HomeIcon fontSize='large' color="secondary"
onClick={() => handleIcon('M')}
/>
<SettingsIcon fontSize='large' color="secondary"
onClick={() => handleIcon('A')}
/>
</div>
</div>
//์์๋ก ๋ฐ์..
//๋ก๊ทธ์ธ๋๋ฉด token๊ฐ๊ณผ payload๊ฐ์ ๋ณด์ฌ์ค๋ค.
{ keycloak.idToken ?
<UserInfo />
: null
}
</>
)
}
menu.jsx ๋ง์ง๋ง์ UserInfo ์ปจํฌ๋ํธ๋ฅผ ์์๋ก ๋ฐ์ํ๋ค.
{ keycloak.idToken ?
<UserInfo />
: null
}
๊ทธ๋ผ ๋ก๊ทธ์ธ์ ํตํด idToken๊ณผ Token , ๊ทธ๋ฆฌ๊ณ payload๊ฐ์ ์ถ๋ ฅํด๋ณธ๋ค.
๋งํฌ์๋น์ค ์ฌ์ดํธ์ ๋ก๊ทธ์ธ์ด ๋์ด token๊ฐ๊ณผ playload์ ๋ณด๋ฅผ ๋ณผ์ ์๋ค.
๋ํ, ๊ฐ๋ฐ์๋ชจ๋ console ์์๋ auth token๊ณผ refresh token์ ๊ณ์์ ์ผ๋ก ์์ฑ๋จ์ ํ์ธํ ์ ์๋ค.
๋์ผํ๊ฒ jwt.io ์ฌ์ดํธ์์๋ ํ์ธํด๋ณธ๋ค.
๋๊ฐ๋ฅผ ๋น๊ตํด๋ณด๋ฉด ๋์ผํ ๊ฒฐ๊ณผ๊ฐ์์ ์์ ์๋ค.
๊ทธ๋ผ token์ ๋ณด์์ ๋ก๊ทธ์ธ ์์ด๋, ์ด๋ฆ ๋ฑ ์ถ๋ ฅํ ๋๋ idTokenParsed ํจ์๋ฅผ ํตํด ๋ค์๊ณผ ๊ฐ์ด ํ๋ฉด ํ๋ค.
์์ด๋ ์ ๋ณด๋ฅผ ์๊ณ ์ถ์๋,
keycloak.idTokenParsed.preferred_username
์์ ๊ฐ์ ํํ๋ก payload๊ฐ์ ์ค ํ๋๋ฅผ ๊ฐ์ ธ์ค๋ฉด ๋๋ค.
๊ทธ๋ผ ๋ค์ํธ์ ๊ณ์..................................
'ํตํฉ์ธ์ฆ์์คํ ๊ตฌ์ถ > KeyCloak' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
react-keycloak ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ useKeycloak Hook๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ํ์ด์ง ํธ์ถ (0) | 2023.05.25 |
---|---|
React ํ์ด์ง์์ SSO(Keycloak)๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ (0) | 2023.05.17 |
Keycloak ์ฌ์ฉ์ ์์ด๋์ ํด๋ผ์ด์ธํธ ์์ฑ (1) | 2023.05.10 |
KeyCloak ์ค์น ๋ฐ ๊ตฌ์ฑ (0) | 2023.05.08 |
Open Source Single Sing-On์ ๋ํด์ ์์๋ณด์ (0) | 2023.05.06 |