如何在含双Vue前端的应用中使用多Keycloak实例与上下文?
我之前刚好帮团队解决过几乎一模一样的多Keycloak上下文共存问题,针对你的Vue+Java架构,给你拆解几个关键的实现步骤:
一、在Vue中初始化完全独立的Keycloak实例
首先要明确:两个前端必须使用完全独立的Keycloak实例,绝对不能共用同一个Keycloak对象。
- 为两个前端分别编写Keycloak配置文件,比如
keycloak-public.js(公开前端)和keycloak-internal.js(内部前端),各自填入对应的Keycloak服务地址、Realm、Client ID:
// keycloak-public.js(公开前端) import Keycloak from 'keycloak-js' export const keycloakPublic = new Keycloak({ url: 'https://your-public-keycloak-domain/auth', realm: 'public-realm', clientId: 'public-frontend-client' })
- 在各自的Vue项目入口(或模块入口)中独立初始化实例,挂载到Vue全局时使用不同命名(比如
$keycloakPublic和$keycloakInternal),避免互相覆盖:
// 公开前端main.js import { keycloakPublic } from './utils/keycloak-public' async function initApp() { await keycloakPublic.init({ onLoad: 'login-required', redirectUri: window.location.origin }) const app = createApp(App) app.config.globalProperties.$keycloak = keycloakPublic app.mount('#app') } initApp()
二、隔离认证状态的存储(核心解决冲突)
默认情况下,Keycloak-js会用localStorage/sessionStorage存储token,两个实例用相同键名会互相覆盖,导致认证状态混乱。解决办法是给每个实例配置自定义存储键:
- 自定义Keycloak的存储适配器,指定不同的键前缀:
// 内部前端初始化时添加adapter配置 await keycloakInternal.init({ onLoad: 'check-sso', adapter: { getToken: () => sessionStorage.getItem('internal-access-token'), setToken: (token) => sessionStorage.setItem('internal-access-token', token), getRefreshToken: () => sessionStorage.getItem('internal-refresh-token'), setRefreshToken: (token) => sessionStorage.setItem('internal-refresh-token', token), getIdToken: () => sessionStorage.getItem('internal-id-token'), setIdToken: (token) => sessionStorage.setItem('internal-id-token', token), clearToken: () => { sessionStorage.removeItem('internal-access-token') sessionStorage.removeItem('internal-refresh-token') sessionStorage.removeItem('internal-id-token') } } })
这样两个前端的认证token会存在不同的存储键下,完全不会互相干扰。
三、独立控制路由守卫和认证逻辑
两个前端的路由守卫要分别绑定各自的Keycloak实例,不要混用:
- 公开前端的路由守卫示例:
router.beforeEach(async (to, from, next) => { const keycloak = router.app.config.globalProperties.$keycloakPublic if (to.meta.requiresAuth) { if (!keycloak.authenticated) { await keycloak.login({ redirectUri: window.location.origin + to.fullPath }) } else { // 可选:自动刷新过期token if (keycloak.isTokenExpired()) { await keycloak.updateToken(5) } next() } } else { next() } })
内部前端的路由守卫同理,只需替换为$keycloakInternal实例即可。
四、Java后端的配合处理
因为两个Keycloak是独立的,Java后端需要能验证来自不同Keycloak的token:
- 在Spring Boot后端中配置两个独立的Keycloak认证提供者,分别对应公开和内部Keycloak的Realm公钥:
// 公开接口的认证配置 @Bean public KeycloakAuthenticationProvider publicKeycloakAuthProvider() { KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider(); provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); return provider; } // 内部接口的认证配置 @Bean public KeycloakAuthenticationProvider internalKeycloakAuthProvider() { KeycloakAuthenticationProvider provider = new KeycloakAuthenticationProvider(); provider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); return provider; }
- 针对不同的接口路径绑定不同的认证过滤器,比如
/public/**对应公开Keycloak认证,/internal/**对应内部Keycloak认证。
最后测试验证
完成配置后,可以通过以下步骤验证:
- 打开公开前端完成登录,查看浏览器存储中是否有带
public-前缀的token; - 打开内部前端访问需认证页面,会跳转到内部Keycloak登录页,登录后存储中会出现带
internal-前缀的token; - 此时两个前端的认证状态完全独立,不会互相影响。
内容的提问来源于stack exchange,提问作者Anothereno
相关产品推荐
相关产品推荐

