You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在vite-module-federation微前端间共享Keycloak实例

微前端架构下Keycloak实例共享方案

一、核心思路:分离状态数据与实例方法

Keycloak实例无法存入Redux(破坏不可变性规则),因此将可序列化的身份核心数据(token、userId、过期时间等)存入Redux实现跨微前端同步,将Keycloak实例的关键方法通过模块联邦封装共享,既保证数据时效性,又满足子应用对实例方法的需求。

二、具体实现步骤

1. 共享可序列化身份数据到Redux

在HostApp初始化Keycloak后,提取可序列化的关键身份数据同步到StoreMF,同时处理令牌自动刷新逻辑:

// store-mf/src/slices/authSlice.js
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  token: null,
  refreshToken: null,
  userId: null,
  tokenExpiry: null,
  refreshTokenExpiry: null,
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    setAuthData: (state, action) => {
      Object.assign(state, action.payload);
    },
    clearAuthData: (state) => {
      Object.assign(state, initialState);
    },
  },
});

export const { setAuthData, clearAuthData } = authSlice.actions;
export default authSlice.reducer;

HostApp初始化并同步数据:

// HostApp.jsx
import { useDispatch } from 'react-redux';
import { setAuthData, clearAuthData } from 'store-mf/authSlice';
import Keycloak from 'keycloak-js';

const keycloak = new Keycloak('/keycloak.json');

const HostApp = () => {
  const dispatch = useDispatch();

  useEffect(() => {
    keycloak.init({ onLoad: 'login-required' }).then(authenticated => {
      if (authenticated) {
        syncAuthDataToRedux();
        // 每分钟检查令牌有效性,自动刷新并同步
        const refreshInterval = setInterval(() => {
          keycloak.updateToken(30).then(refreshed => {
            if (refreshed) syncAuthDataToRedux();
          }).catch(() => {
            dispatch(clearAuthData());
            clearInterval(refreshInterval);
          });
        }, 60000);
        return () => clearInterval(refreshInterval);
      }
    });
  }, [dispatch]);

  const syncAuthDataToRedux = () => {
    dispatch(setAuthData({
      token: keycloak.token,
      refreshToken: keycloak.refreshToken,
      userId: keycloak.subject,
      tokenExpiry: keycloak.tokenParsed?.exp,
      refreshTokenExpiry: keycloak.refreshTokenParsed?.exp,
    }));
  };

  return (
    <StoreProvider store={StoreMF.store}>
      <HeaderMF />
      <FooterMF />
    </StoreProvider>
  );
};

2. 通过模块联邦共享Keycloak实例方法

将Keycloak实例封装成独立联邦模块,仅暴露必要方法(避免直接共享整个实例):

// auth-mf/src/keycloakService.js
import Keycloak from 'keycloak-js';

let keycloakInstance = null;

export const initKeycloak = (config) => {
  keycloakInstance = new Keycloak(config);
  return keycloakInstance.init({ onLoad: 'login-required' });
};

// 仅暴露业务需要的方法,最小权限原则
export const getAuthMethods = () => {
  if (!keycloakInstance) {
    throw new Error('Keycloak未初始化,请先调用initKeycloak');
  }
  return {
    logout: () => keycloakInstance.logout(),
    updateToken: (minValidity) => keycloakInstance.updateToken(minValidity),
    isAuthenticated: () => keycloakInstance.authenticated,
  };
};

在vite配置中暴露该模块:

// auth-mf/vite.config.js
import { defineConfig } from 'vite';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    federation({
      name: 'authMF',
      filename: 'remoteEntry.js',
      exposes: {
        './authService': './src/keycloakService.js',
      },
      shared: ['react', 'react-dom', 'keycloak-js'],
    }),
  ],
});

子应用通过模块联邦导入方法使用:

// HeaderMF.jsx
import { getAuthMethods } from 'authMF/authService';

const HeaderMF = () => {
  const handleLogout = () => {
    const { logout } = getAuthMethods();
    logout();
  };

  return (
    <header>
      <button onClick={handleLogout}>退出登录</button>
    </header>
  );
};

3. 子应用独立API请求的身份认证

子应用通过RTK Query发起请求时,自动从Redux获取token注入请求头:

// 子应用/src/services/apiSlice.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const baseQuery = fetchBaseQuery({
  baseUrl: '/api',
  prepareHeaders: (headers, { getState }) => {
    const token = getState().auth.token;
    if (token) {
      headers.set('Authorization', `Bearer ${token}`);
    }
    return headers;
  },
});

export const apiSlice = createApi({
  baseQuery,
  endpoints: (builder) => ({
    getUserInfo: builder.query({
      query: (userId) => `/users/${userId}`,
    }),
  }),
});

export const { useGetUserInfoQuery } = apiSlice;

三、安全性保障

  • 最小权限控制:仅暴露Keycloak实例的必要方法,禁止子应用直接操作完整实例,避免恶意篡改。
  • 令牌自动刷新:HostApp统一管理令牌生命周期,自动刷新并同步到Redux,确保子应用获取的token始终有效。
  • Redux状态合规:仅存储可序列化的原始数据,严格遵守Redux不可变性规则,避免状态污染。
  • 请求头自动注入:子应用API请求无需手动管理token,通过RTK Query自动从Redux取数注入,减少人为错误。
  • 依赖版本统一:将Keycloak作为模块联邦共享依赖,确保所有微前端使用同一版本,避免兼容性与安全漏洞问题。

内容的提问来源于stack exchange,提问作者Patryk Gajewski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:12:25