Keycloak์ ์ฌ์ฉํ๋๋ฐ
ํ์ํ ๊ธฐ์ด ํ์ผ์ ๋๋ถ๋ถ ์์ฑํ๋ค.
๊ทธ๋ฌ๋ฉด ๋ง๋ ํ์ผ์ ์ด๋ป๊ฒ ์ฌ์ฉํ๋๊ฑธ๊น?
[ ์ด์ ํฌ์คํ - keycloak์ ์ค์ ํ์ผ ์์ฑํ๊ธฐ ]
[Keycloak, Docker, React] React์์ keycloak์ ๋ ์์ธํ๊ฒ ๋ค๋ค๋ณด์.
์ ํฌ์คํ ์์ keycloak๊ณผ React์๊ธฐ๋ณธ์ ์ธ ์ฐ๊ฒฐ๊น์ง ํ๋ค.์ด๋ฒ์ ํ ํฐ์ ๊ฐ์ ธ์ค๊ณ ,์ฌ์ฉ์์ ๊ถํ์ ๋ฐ๋ผ ํ์ด์ง์ ์ถ์ ๊ตฌ๋ฅผ ๊ด๋ฆฌํ์. [ Keycloak ์ค์ ํ๊ธฐ ] [Keycloak, React, Docker] React์์ Keycloak์ ๋ณธ
post-this.tistory.com
1. PemissionSetting.jsx_ ๊ด๋ฆฌ์๋ฅผ ๊ด๋ฆฌํ๋ ํ์ด์ง
- ์ด ํ์ด์ง๋ ์ผ๋ฐ admin์ ๊ด๋ฆฌํ๋ ํ์ด์ง๋ก, super admin๋ง ๋ค์ด๊ฐ ์ ์๋๋ก ํ๊ณ ์๋ค.
- ์ด ํ์ด์ง์ ๋ค์ด๊ฐ๊ธฐ ์ํด์ Keycloak์ผ๋ก ๋ก๊ทธ์ธ์ ํด์ผํ๋ค.
- ์ฌ์ฉ์์ ์ญํ ์ด super admin์ธ์ง ํ์ธ๋์ด์ผ ๊ด๋ฆฌ์ ์ ๋ณด๊ฐ ์ถ๋ ฅ๋๋๋ก ํ๋ค.
- keycloak๊ณผ ๊ด๋ จ๋ ํ์ผ๋ค importํ๊ธฐ
// Keycloak๊ณผ ๊ด๋ จ๋ ํ์ผ์ import ํ๋ค.
// authFetch๋ Token์ ํค๋์ ๋ถ์ด๋ ์ญํ ์ ํ๋ค.
import { authFetch } from "../../auth/authFetch.js";
// useAuth๋ keycloak์ ์ํ(๋ก๊ทธ์ธํ๋์ง, ๋๊ตฌ์ธ์ง)๋ฅผ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๊ฐ ์ ์ ์๊ฒ ํ๋ค.
import { useAuth } from "../../auth/AuthProvider.jsx";
- ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด auth ์ ์ธ
export default function PermissionSetting() {
// useAuth ์ฌ์ฉํ๊ฒ ๋ค!
// useAuth๋ AuthContext๋ผ๋ ๋ฐ์ดํฐ ์ ์ฅ์์ ์ ๊ทผ๊ฐ๋ฅํจ
const auth = useAuth();
Q. useAuth๊ฐ ์ด๋ป๊ฒ AuthContext์ ์ ๊ทผ๊ฐ๋ฅํ๊ฐ?
A. useAuth๋ AuthProvider๊ฐ ๊ฐ์ ธ์จ keycloak ์ ๋ณด๋ค์ AuthContext์ ์ง์ด๋ฃ์๋ค. ๊ทธ๋์ useAuth๋ ๊ทธ๋๋ก ๊บผ๋ด ์ด๋ค. AuthProvider์ ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด ๋ ์ฝ๊ฒ ์ดํด๊ฐ๋ฅํ๋ค.
// AuthProvider ์ฝ๋ ์ผ๋ถ // AuthProvider๊ฐ keycloak ์ ๋ณด๋ฅผ ๋ฃ์๋ค.(AuthContext) return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;โ
// useAuth ์ฝ๋ ์ผ๋ถ // useAuth๊ฐ ๋ค๊ณ ์จ ์ ๋ณด๋ฅผ ๊ฐ์ ธ๊ฐ๋ค.(AuthContext๋ฅผ context๋ก ๊ฐ์ ธ์ด) const context = useContext(AuthContext); return context;
- ๊ด๋ฆฌ์ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ์กฐ๊ฑด๋ฌธ
useEffect(() => {
// auth.loading์ ํคํด๋ก์ด ์๋ฒ์ ํต์ ํ๋ฉด์ ์ฌ์ฉ์๊ฐ ๋๊ตฌ์ธ์ง ํ์ธ ์ค์ธ ์ํ์ด๋ค.
// auth.loading์ ๋ก๋ฉ ์ค์ด ์๋์ง ๋ฌป๋๋ค. true -> ์กฐํ์ค false -> ์กฐํ๋
// auth.superAdmin์ ํคํด๋ก ํ ํฐ ์์ ๋ค์ด์๋ Role ์ ๋ณด๋ฅผ ํ์ธํด์ ์ด ์ฌ๋์ด ์ต๊ณ ๊ด๋ฆฌ์์ธ์ง ํ๋จํ๋ค.
// keycloak์ด ํ์ธ์ ํ ๋ค ๊ทธ ๊ฒฐ๊ณผ๊ฐ ์ต๊ณ ๊ด๋ฆฌ์์ผ ๋๋ง ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๋ก์ง์ด๋ค.
if (!auth.loading && auth.superAdmin) {
// ๋ชจ๋ true๊ฐ ๋์ค๋ฉด loadAmdins๊ฐ ์คํ๋์ด ๊ด๋ฆฌ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
loadAdmins();
}
}, [auth.loading, auth.superAdmin]); // ์ด ๋ ๊ฐ์ด ๋ณํ ๋๋ง๋ค ๋ก์ง ๋ค์ ์ฒดํฌํ๊ธฐ
Q. !auth.loading์ ์ด ์ด์ ๊ฐ ๋ญ๊น? ๊ทธ๋ฅ auth.superAmdin๋ง ๊ฒ์ฌํ๋ฉด ๋๋๊ฑฐ ์๋๊น?
A. !auth.loading์ ํ์คํ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ๋๊น์ง ํ๋ฉด์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ๊ธฐ๋ค๋ฆฌ๊ฒ ํ๋ค. ๊ทธ๋๊น ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ด ์ ๋๋ก ๋๋์ง ์์ ํ ํ์ธ์ด ๋๋๋ฉด ๊ทธ ํ์ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๊ฐ superAdmin์ด ๋ง๋์ง ํ์ธํ๋ ๋ก์ง์ ๊ฐ์ง๋ค. !auth.loading์ ์์ ์ผ๋ฉด ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ด ๋์ด์๋ ์ํ์ธ๋ฐ๋ keycloak๊ณผ ์๋ฒ์ ์ ๋ฌ์ด ์๋ฒฝํ๊ฒ ์ด๋ค์ง์ง ์์์ isAuthenticated๊ฐ ์ ์ false์ผ ์ ์๋ค. ๊ทธ๋ ๊ฒ๋๋ฉด ์ฌ์ฉ์๋ ๋ก๊ทธ์ธ์ ํ์์๋, ๋ก๊ทธ์ธ ํ๋ฉด์ด ์ ์ ๋ํ๋๋ ํ์์ด ๋ฐ์ํ ์ ์๋ค.
- ๊ฐ ๊ธฐ๋ฅ๋ค
๋ชจ๋ authFetch๋ฅผ ์ฌ์ฉํ๋ค.
// ๊ฒ์
const handleSearch = async (keyword) => {
try {
// ์๋ฒ /api/amdins๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฌ๋ผ๊ณ ํ ๋, ํคํด๋ก์ด ๋ฐ๊ธํด์ค Bearer ํ ํฐ์ด ํจ๊ป ๋ ์๊ฐ๋ค.
const response = await authFetch(`/api/admins/search?keyword=${encodeURIComponent(keyword)}`);
// ์ญ์
const handleDelete = async (id) => {
if (!window.confirm("์ ๋ง ์ญ์ ํ์๊ฒ ์ต๋๊น?")) {
return;
}
try {
// ♦์๋ฒ /api/amdins๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฌ๋ผ๊ณ ํ ๋, ํคํด๋ก์ด ๋ฐ๊ธํด์ค Bearer ํ ํฐ์ด ํจ๊ป ๋ ์๊ฐ๋ค.
const response = await authFetch(`/api/admins/${id}`, { method: "DELETE" });
2. CustomerTable.jsx_ ๊ณ ๊ฐ์ ๊ด๋ฆฌํ๋ ํ์ด์ง
- ์ด ํ์ด์ง๋ VIP ๋ฑ๊ธ ์ด์์ ๊ณ ๊ฐ์ ๊ด๋ฆฌํ๋ ํ์ด์ง๋ค.
- ๊ณ ๊ฐ ๊ด๋ฆฌ ํ์ด์ง๋ ์กฐํ | ์ถ๊ฐ | ์์ | ๊ฒ์ | ์ญ์ ๊ธฐ๋ฅ์ด ์๋ค.
- ๊ด๋ฆฌ์๋ ๊ฐ๊ณ ์๋ ๊ถํ์ ๊ธฐ๋ฅ๋ง ์ฌ์ฉํ ์ ์๋ค.
ex) A๊ด๋ฆฌ์๋ ์กฐํ | ์ถ๊ฐ | ์์ ๊ถํ๋ง ์์ผ๋ฉด, ๊ฒ์ | ์ญ์ ๋ ๋ถ๊ฐ๋ฅํ๋ค. - PermissionSetting.jsx๋ ๋๊ฐ์ด importํ๊ณ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
// AuthProvider๋ฅผ ํตํด ํคํด๋ก ์๋ฒ์์ ๋ฐ์์จ ์ฌ์ฉ์์ ์ ๋ณด์ ๊ถํ ๋ฆฌ์คํธ๋ฅผ auth ๋ณ์์ ๋๋ค.
const auth = useAuth();
- auth ๊ถํ ์ฒดํฌ
// ex) ์ต๊ณ ๊ด๋ฆฌ์ || ์ฝ๊ธฐ๊ด๋ฆฌ๊ถํ ์ค ํ๋๋ง ๊ฐ๊ณ ์์ผ๋ฉด ํจ์ค!
// ๋ ์ค ํ๋๋ง true์ฌ๋ canRead์ true๊ฐ ๋ค์ด๊ฐ๋ค.
const canRead = auth.superAdmin || auth.customerRead;
const canSearch = auth.superAdmin || auth.customerSearch;
const canAdd = auth.superAdmin || auth.customerAdd;
const canEdit = auth.superAdmin || auth.customerEdit;
const canDelete = auth.superAdmin || auth.customerDelete;
- ๊ฐ ๊ธฐ๋ฅ๋ค
๋ชจ๋ authFetch๋ฅผ ์ฌ์ฉํ๋ค.
const loadCustomers = async () => {
if (!canRead) return;
try {
// ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ฌ ๋(์ฝ๊ธฐ ๊ถํ), authFetch๋ก ํ ํฐ์ ๋ฃ์ด์ ๋ณด๋ธ๋ค. => ๋ค๋ฅธ ๋์๋ ๋ง์ฐฌ๊ฐ์ง
const res = await authFetch("/api/customers");
if (res.ok) {
const data = await res.json();
dispatch({ type: "INIT", payload: data });
setPage(1);
}
}