如何将您的应用以 iframe 形式集成到 B2CORE
了解如何将第三方 Web 应用以 iframe 形式集成到 B2CORE,包括服务器配置、主题和语言同步,以及通过 postMessage API 进行基于 JWT 的身份验证
B2CORE 支持通过 iframe 将第三方 Web 应用直接嵌入客户 UI。当自定义菜单项配置为 Iframe 行为时,B2CORE 会在界面中加载您的应用,为客户提供无需离开平台的无缝体验。
本指南涵盖两个方面:在 B2CORE Back Office 中配置自定义菜单项,以及准备您的应用以确保其能在 B2CORE iframe 中正常运行。
前提条件
在继续之前,请确保满足以下条件:
- 您的应用可通过 HTTPS 访问。
- 您拥有可访问 B2CORE Back Office 且具有
Update menu权限的账户(详情请参阅如何添加用户组并授予权限)。 - 您熟悉如何添加自定义菜单项流程。
第 1 步:配置您的服务器以允许 iframe 嵌入
默认情况下,大多数 Web 服务器和框架会阻止页面被嵌入其他域中的 iframe。要允许 B2CORE 加载您的应用,您必须在服务器上配置相应的 HTTP 响应标头。
您需要设置以下标头中的一个或两个:
Content-Security-Policy
带有 frame-ancestors 指令的 Content-Security-Policy 标头用于控制哪些源可嵌入您的页面。设置此标头以包含您的 B2CORE 实例源:
Content-Security-Policy: frame-ancestors 'self' https://portal.example.com将 https://portal.example.com 替换为客户所使用的 B2CORE UI 的实际源。如果您的应用需要嵌入多个 B2CORE 实例,可以指定多个以空格分隔的源。
有关更多信息,请参阅 MDN 上的 Content-Security-Policy 文档。
X-Frame-Options
X-Frame-Options 标头是一种可实现类似效果的旧机制。如果您使用它,请将其设置为带有您的 B2CORE 源的 ALLOW-FROM:
X-Frame-Options: ALLOW-FROM https://portal.example.com并非所有浏览器都支持 X-Frame-Options: ALLOW-FROM 指令。建议将带有 frame-ancestors 指令的 Content-Security-Policy 标头用作主要机制,并仅将 X-Frame-Options 作为对旧版客户端的后备方案。
有关更多信息,请参阅 MDN 上的 X-Frame-Options 文档。
第 2 步:在 B2CORE Back Office 中添加自定义菜单项
要让客户能够访问您的应用,请添加一个具有 iframe 行为的自定义菜单项:
导航至 Promotion > Menu。
点击 General 行中的眼睛图标以查看菜单树。
点击页面右上角的 +Create。
填写必填字段:
- 在 Name 字段中,输入菜单项的唯一名称。
- 在 Caption 字段中,输入客户将在菜单中看到的标签。
- 在 External URL 字段中,指定您的应用 URL。
- 在 Icon 字段中,指定 SVG 图标的 URL(16x16 px、单色、透明背景)。
- 在 Custom Behavior 下拉列表中,选择 Iframe。
如有需要,配置可选限制:
- 若要将可见性限制为特定验证级别,请在 Verification Level Allowance 下拉列表中选择相应级别。
- 若要将可见性限制为特定客户类型,请在 Client Type Allowance 下拉列表中选择相应类型。
启用 Visible 复选框,使该项目显示在菜单中。
点击 Save 以添加自定义菜单项。
当客户点击此菜单项时,您的应用将在 B2CORE UI 内的 iframe 中加载。
自定义菜单项还提供另外两种行为选项:Same tab(在当前浏览器标签页中打开 URL)和 New tab(在新的浏览器标签页中打开 URL)。iframe 选项是唯一可将您的应用嵌入 B2CORE 界面内的选项。
第 3 步(可选):实现 postMessage 通信协议
如果您的应用需要识别经过身份验证的 B2CORE 用户、匹配 B2CORE UI 主题,或遵循用户选择的语言,您可以实现下述 postMessage 通信协议。此步骤为可选步骤——如果您的应用不需要用户身份验证、主题同步或语言同步,可以跳过此步骤。
消息参考
从您的应用发送至 B2CORE 的消息
| 消息类型 | 描述 | 载荷 |
|---|---|---|
embed-iframe-ready | 表示 iframe 已加载完毕并准备好接收消息。 | { type: "embed-iframe-ready" } |
embed-request-jwt-token | 向 B2CORE 请求 JWT 身份验证令牌。 | { type: "embed-request-jwt-token" } |
从 B2CORE 发送至您的应用的消息
| 消息类型 | 描述 | 载荷 |
|---|---|---|
embed-theme-change | 每当 B2CORE UI 主题变更时发送(并会在 embed-iframe-ready 后立即发送一次)。 | { type: "embed-theme-change", theme: "dark-theme" | "light-theme" } |
embed-language-change | 每当 B2CORE UI 语言变更时发送(并会在 embed-iframe-ready 后立即发送一次)。 | { type: "embed-language-change", lang: "<language-code>" } |
embed-jwt-token | 对令牌请求的成功响应。 | { type: "embed-jwt-token", token: "<jwt-string>", expiresAt: "<ISO-8601>" } |
embed-jwt-token-error | 令牌生成失败时的错误响应。 | { type: "embed-jwt-token-error", error: "<error-message>" } |
embed-logout | 当 B2CORE 用户退出登录时发送。此前向您的应用签发的任何令牌均不再有效。 | { type: "embed-logout" } |
lang 字段包含与 B2CORE UI 当前所选语言对应的小写 ISO 639-1 语言代码(例如 en、de、ar)。
通过 embed-jwt-token 签发的 JWT 令牌绑定于签发时已登录的 B2CORE 用户。当该用户退出登录时,B2CORE 会发送 embed-logout,此后不得再使用该令牌。收到此消息后,请丢弃所有缓存令牌、停止计划的刷新任务,并清除用户特定状态。如果之后有其他用户登录,请使用 embed-request-jwt-token 请求新令牌——系统将为新用户签发全新的令牌。
通信流程
您的应用与 B2CORE 之间的消息顺序遵循以下模式:
sequenceDiagram
participant App as Your App (iframe)
participant B2CORE as B2CORE (host)
App->>B2CORE: embed-iframe-ready
B2CORE-->>App: embed-theme-change (initial theme)
B2CORE-->>App: embed-language-change (initial language)
App->>B2CORE: embed-request-jwt-token
alt success
B2CORE-->>App: embed-jwt-token
else failure
B2CORE-->>App: embed-jwt-token-error
end
Note over App,B2CORE: Token nears expiry...
App->>B2CORE: embed-request-jwt-token
B2CORE-->>App: embed-jwt-token
Note over App,B2CORE: User toggles theme...
B2CORE-->>App: embed-theme-change
Note over App,B2CORE: User changes language...
B2CORE-->>App: embed-language-change
Note over App,B2CORE: User logs out...
B2CORE-->>App: embed-logout发送就绪信号。 当您的页面完成加载时,向 B2CORE 发送 embed-iframe-ready 消息。这会告知宿主您的应用已准备好接收数据。
接收当前主题。 B2CORE 会响应包含当前主题(dark-theme 或 light-theme)的 embed-theme-change 消息。将该主题应用于您的 UI。用户切换主题时,您也会收到此消息。
接收当前语言。 B2CORE 还会响应包含 B2CORE UI 当前所选语言代码(例如 en 或 ar)的 embed-language-change 消息。将相应区域设置应用于您的 UI。用户更改语言时,您会再次收到此消息。
请求身份验证令牌。 当您需要识别当前用户时,发送 embed-request-jwt-token。如果令牌生成成功,B2CORE 会响应 embed-jwt-token(包含 JWT 及其过期时间);如果令牌生成失败,则会响应 embed-jwt-token-error。
在令牌过期前刷新令牌。 JWT 的过期时间在 expiresAt 字段中提供(ISO 8601 格式)。在当前令牌过期前请求新令牌,以保持不中断的访问。
处理退出登录。 当 B2CORE 用户退出登录时,B2CORE 会发送 embed-logout。收到后,请丢弃缓存的令牌、取消所有计划的刷新任务,并清除用户特定状态,以免数据泄露给下一位用户。如果之后有其他用户登录,请使用 embed-request-jwt-token 请求新的令牌。
代码示例
您可以在应用中包含以下完整 JavaScript 代码片段:
(function () {
const B2CORE_ORIGIN = '*'; // Replace with your B2CORE instance origin for production
let currentToken = null;
let tokenRefreshTimer = null;
// --- Send a message to B2CORE ---
function sendToHost(message) {
window.parent.postMessage(message, B2CORE_ORIGIN);
}
// --- Handle incoming messages from B2CORE ---
function handleMessage(event) {
const data = event.data;
if (!data || !data.type) return;
switch (data.type) {
case 'embed-theme-change':
applyTheme(data.theme);
break;
case 'embed-language-change':
applyLanguage(data.lang);
break;
case 'embed-jwt-token':
handleToken(data.token, data.expiresAt);
break;
case 'embed-jwt-token-error':
console.error('Token error from B2Core:', data.error);
break;
case 'embed-logout':
handleLogout();
break;
}
}
// --- Apply theme to your UI ---
function applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
}
// --- Apply language to your UI ---
function applyLanguage(lang) {
document.documentElement.setAttribute('lang', lang);
}
// --- Handle received JWT token ---
function handleToken(token, expiresAt) {
currentToken = token;
// Schedule a refresh 2 minutes before expiry
if (tokenRefreshTimer) {
clearTimeout(tokenRefreshTimer);
}
const refreshIn = new Date(expiresAt).getTime() - Date.now() - 2 * 60 * 1000;
if (refreshIn > 0) {
tokenRefreshTimer = setTimeout(requestToken, refreshIn);
} else {
requestToken();
}
}
// --- Request a JWT token from B2CORE ---
function requestToken() {
sendToHost({ type: 'embed-request-jwt-token' });
}
// --- Handle B2CORE user logout ---
function handleLogout() {
// The token is bound to the user who just logged out — stop using it.
currentToken = null;
if (tokenRefreshTimer) {
clearTimeout(tokenRefreshTimer);
tokenRefreshTimer = null;
}
// Clear any user-specific state in your app here.
}
// --- Initialize ---
window.addEventListener('message', handleMessage);
sendToHost({ type: 'embed-iframe-ready' });
requestToken();
})();在生产环境中,请将 B2CORE_ORIGIN 值替换为您的 B2CORE 实例实际源(例如 'https://portal.example.com')。仅在开发期间可以使用 '*',因为它允许任何源与您的应用通信。
TypeScript 类型定义
如果您的应用使用 TypeScript 构建,可以使用以下类型定义:
type EmbedTheme = 'dark-theme' | 'light-theme';
// Messages: Your App -> B2CORE
interface EmbedIframeReadyMessage {
readonly type: 'embed-iframe-ready';
}
interface EmbedRequestJwtTokenMessage {
readonly type: 'embed-request-jwt-token';
}
type EmbedOutboundMessage = EmbedIframeReadyMessage | EmbedRequestJwtTokenMessage;
// Messages: B2CORE -> Your App
interface EmbedThemeChangeMessage {
readonly type: 'embed-theme-change';
readonly theme: EmbedTheme;
}
interface EmbedLanguageChangeMessage {
readonly type: 'embed-language-change';
readonly lang: string; // ISO 639-1 code, e.g. "en", "de", "ar"
}
interface EmbedJwtTokenMessage {
readonly type: 'embed-jwt-token';
readonly token: string;
readonly expiresAt: string; // ISO 8601
}
interface EmbedJwtTokenErrorMessage {
readonly type: 'embed-jwt-token-error';
readonly error: string;
}
interface EmbedLogoutMessage {
readonly type: 'embed-logout';
}
type EmbedInboundMessage =
| EmbedThemeChangeMessage
| EmbedLanguageChangeMessage
| EmbedJwtTokenMessage
| EmbedJwtTokenErrorMessage
| EmbedLogoutMessage;第 4 步(可选):验证 JWT 令牌
B2CORE 签发的 JWT 令牌可针对 B2CORE API(Admin Panel 后端)公开的 JSON Web Key Set (JWKS) 端点进行验证。这通常是 API/admin 域,而不是面向客户的 UI 域:
https://api.<your-domain>/.well-known/jwks.json使用此端点获取验证令牌签名所需的公钥。大多数 JWT 库都原生支持基于 JWKS 的验证。
在开发期间进行手动检查时,您可以使用 JWT 解码器工具解码和验证 JWT 令牌。
最后更新于