הטמעת לוחות מחוונים של Superset: Guest Token ו-RLS
נתקלנו במצב: לקוח – סטארטאפ פינטק עם 5 מחלקות (מכירות, שיווק, פיננסים, משאבי אנוש, תמיכה). כל מחלקה רצתה לראות את הניתוחים שלה בפורטל BI משותף, אבל הנתונים היו מופרדים בקפדנות. Apache Superset הוא כלי קוד פתוח חזק, אבל מחוץ לקופסה לא ניתן להטמיע אותו באפליקציה של צד שלישי ללא הגדרות נוספות. ללא הגדרה נכונה של Guest Token ו-Row Level Security (RLS), לוחות מחוונים או רואים את כל הנתונים או נכשלים בטעינה עקב שגיאות CORS. פתרנו את הבעיה על ידי הטמעת Superset באמצעות Embedded SDK, ועכשיו אנחנו חולקים את הניסיון שלנו. ההתקנה ארכה 3 ימים; התוצאה – כל מחלקה רואה רק את המדדים שלה, ו-IT מנהל גישה מרכזית. חיסכון ברישיונות בהשוואה לפתרונות BI בתשלום יכול לנוע בין $3k–5k לשנה.
אילו בעיות פותר הטמעת Superset?
נקודת הכאב העיקרית היא הפרדת נתונים בין מחלקות. ללא RLS, נאלצנו ליצור לוחות מחוונים נפרדים לכל מחלקה, מה שהגדיל את זמן הפיתוח בשבועיים והפך את המערכת לבלתי גמישה. Superset עם Embedded SDK מאפשר להטמיע לוח מחוונים אחד ולסנן נתונים דינמית באמצעות Guest Token. בנוסף, אנחנו פותרים:
- שגיאות CORS – הגדרה שגויה חוסמת טעינת לוח המחוונים.
- ניהול גישה – מרכזי דרך האפליקציה שלך.
- ביצועים – זמן טעינת לוח מחוונים ממוצע ירד ב-40% לאחר הגדרת מטמון.
איך עובדת הטמעה באמצעות Embedded SDK?
Superset משתמש ב-Embedded SDK וב-Guest Token להטמעה מאובטחת. ה-Guest Token הוא מפתח JWT זמני המקושר למשתמש וללוח מחוונים. כפי שנאמר בתיעוד הרשמי של Superset: Guest Token הוא JWT מוגבל בזמן המשמש להטמעת לוחות מחוונים בצורה מאובטחת. אנו מגדירים נקודת קצה באפליקציה שלנו שמנפיקה את הטוקן דרך ה-API של Superset. בניגוד ל-Metabase, שבו צריך להגדיר פרוקסי JWT, Superset מאפשר להעביר תנאי RLS ישירות בטוקן. זה מספק גמישות: נתונים מסוננים ברמת שאילתת SQL.
הגדרת Superset
ב-superset_config.py:
FEATURE_FLAGS = {
"EMBEDDED_SUPERSET": True,
"ENABLE_TEMPLATE_PROCESSING": True
}
CORS_OPTIONS = {
'supports_credentials': True,
'origins': ['https://your-app.com']
}
SESSION_COOKIE_SAMESITE = None
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
נקודות מפתח: FEATURE_FLAGS = { "EMBEDDED_SUPERSET": True, "ENABLE_TEMPLATE_PROCESSING": True } CORS_OPTIONS = { 'supports_credentials': True, 'origins': ['https://your-app.com'] } SESSION_COOKIE_SAMESITE = None SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True מאפשר הטמעה; CORS – רק הדומיין שלך; עוגיות EMBEDDED_SUPERSET נדרשות עבור iframes.
אפשרויות CORS נוספות
אם הפרונטאנד שלך נמצא בתת-דומיין, ציין אותו ב-`origins`. עבור ייצור, הוסף `'methods': ['GET', 'POST']` ו-`'allow_headers': ['Content-Type', 'Authorization']`.יצירת Guest Token
async function getSupersetGuestToken( dashboardId: string, userId: string, userEmail: string ): Promise<string> { // Получить admin access token
const loginResponse = await fetch(`${SUPERSET_URL}/api/v1/security/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: process.env.SUPERSET_ADMIN_USER,
password: process.env.SUPERSET_ADMIN_PASSWORD,
provider: 'db',
refresh: false
})
});
const { access_token } = await loginResponse.json();
// Получить Guest Token для конкретного дашборда
const guestResponse = await fetch(`${SUPERSET_URL}/api/v1/security/guest_token/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${access_token}`
},
body: JSON.stringify({
user: {
username: userId,
first_name: userEmail.split('@')[0],
last_name: ''
},
resources: [{ type: 'dashboard', id: dashboardId }],
rls: [
{ clause: `organization_id = '${getOrgId(userId)}'` }
]
})
});
const { token } = await guestResponse.json();
return token;
}שים לב: אנו משתמשים ב-SameSite=None עם async function getSupersetGuestToken( dashboardId: string, userId: string, userEmail: string ): Promise<string> { // Получить admin access token const loginResponse = await fetch(`${SUPERSET_URL}/api/v1/security/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: process.env.SUPERSET_ADMIN_USER, password: process.env.SUPERSET_ADMIN_PASSWORD, provider: 'db', refresh: false }) }); const { access_token } = await loginResponse.json(); // Получить Guest Token для конкретного дашборда const guestResponse = await fetch(`${SUPERSET_URL}/api/v1/security/guest_token/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${access_token}` }, body: JSON.stringify({ user: { username: userId, first_name: userEmail.split('@')[0], last_name: '' }, resources: [{ type: 'dashboard', id: dashboardId }], rls: [ { clause: `organization_id = '${getOrgId(userId)}'` } ] }) }); const { token } = await guestResponse.json(); return token; } דינמי. זה מבטיח שמשתמשים מחברות שונות לא יראו את הנתונים של זה.
רכיב React באמצעות SDK
npm install @superset-ui/embedded-sdk import { embedDashboard } from '@superset-ui/embedded-sdk';
import { useEffect, useRef } from 'react';
function SupersetDashboard({ dashboardId }) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const embed = embedDashboard({
id: dashboardId,
supersetDomain: process.env.NEXT_PUBLIC_SUPERSET_URL,
mountPoint: containerRef.current,
fetchGuestToken: () =>
fetch(`/api/superset/guest-token?dashboardId=${dashboardId}`)
.then(r => r.json())
.then(d => d.token),
dashboardUiConfig: {
hideTitle: true,
hideTab: false,
filters: {
expanded: false
}
}
});
return () => embed.unmount();
}, [dashboardId]);
return (
<div
ref={containerRef}
className="superset-container w-full rounded-xl overflow-hidden"
style={{ height: '600px' }}
/>
);
}ניתן לעשות שימוש חוזר ברכיב עבור כל לוח מחוונים – פשוט העבר את המזהה.
איך להגדיר Row Level Security?
דרך rls ב-Guest Token, שאילתות ב-Superset מסוננות אוטומטית ברמת SQL. Superset מוסיף organization_id לכל שאילתה. המשתמש פיזית לא יכול לראות נתונים של ארגונים אחרים. זו חלופה להגדרת תפקידים נפרדים ב-Superset – אנו מנהלים גישה מרכזית מהאפליקציה שלנו. בפרויקט אחד, RLS צמצם את זמן הפרדת הגישה משבועיים ליומיים.
מתי לבחור ב-Superset על פני Metabase?
| קריטריון | Superset | Metabase |
|---|---|---|
| הטמעה | Embedded SDK + Guest Token | פרוקסי JWT או תוכנית בתשלום |
| RLS | דרך Guest Token (ברמת SQL) | מוגדר בתוך Metabase |
| רישיון | Apache 2.0 (חינם) | AGPL (Enterprise בתשלום) |
| ביצועים | איטי יותר בשאילתות מורכבות | מהיר יותר ללוחות מחוונים פשוטים |
Superset מנצח בגמישות התאמה אישית ובעלות – חיסכון ברישיונות בהשוואה ל-BI בתשלום יכול לנוע בין $3k–5k לשנה. אם אתה צריך ניתוחים פשוטים ללא RLS מורכב, Metabase קל יותר להגדרה. אבל עבור התאמה אישית עמוקה והפרדת נתונים, Superset הוא הבחירה האופטימלית.
תהליך ולוח זמנים
- ניתוח – אנו לומדים את לוחות המחוונים ותפקידי המשתמשים שלך.
- הגדרת Superset – CORS, Guest Token, הגדרת RLS.
- פיתוח backend – נקודת קצה להנפקת טוקן (בדרך כלל Node.js או Django).
- אינטגרציית SDK – הטמעת רכיב ב-React/Vue/Angular.
- בדיקות – אימות זכויות גישה וטעינה.
- פריסה – הגדרת CI/CD וניטור.
| שלב | משך |
|---|---|
| ניתוח | יום אחד |
| הגדרת Superset | יום אחד |
| פיתוח backend | 1–2 ימים |
| אינטגרציית SDK | יום אחד |
| בדיקות | יום אחד |
| פריסה | 0.5 יום |
כתוצאה מכך, אתה מקבל תיעוד הגדרות, API להנפקת טוקן, סכמת RLS, קוד רכיב מוכן והדרכה לצוות. תמיכה למשך שבועיים לאחר ההשקה.
טעויות הטמעה נפוצות
- התעלמות מ-CORS – לוח המחוונים לא נטען. ודא ש-
npm install @superset-ui/embedded-sdkמכיל את הדומיין המדויק של האפליקציה כולל פרוטוקול. import { embedDashboard } from '@superset-ui/embedded-sdk'; import { useEffect, useRef } from 'react'; function SupersetDashboard({ dashboardId }) { const containerRef = useRef<HTMLDivElement>(null); useEffect(() => { if (!containerRef.current) return; const embed = embedDashboard({ id: dashboardId, supersetDomain: process.env.NEXT_PUBLIC_SUPERSET_URL, mountPoint: containerRef.current, fetchGuestToken: () => fetch(`/api/superset/guest-token?dashboardId=${dashboardId}`) .then(r => r.json()) .then(d => d.token), dashboardUiConfig: { hideTitle: true, hideTab: false, filters: { expanded: false } } }); return () => embed.unmount(); }, [dashboardId]); return ( <div ref={containerRef} className="superset-container w-full rounded-xl overflow-hidden" style={{ height: '600px' }} /> ); }שגוי עבור עוגיות – אם לא מוגדר ל-rls, ה-iframe יחסום עוגיות.- תנאי RLS שגויים – ללא אימות, משתמשים עלולים לראות נתונים של אחרים. תמיד ודא שה-
WHERE organization_id = 'user-org-id'מוחלף כראוי. - ללא טיפול בפגיית Guest Token – לטוקן יש תוחלת חיים מוגבלת (ברירת מחדל 30 דקות). הגדר רענון אוטומטי בצד הלקוח.
צור קשר – אנו נעריך את הפרויקט שלך ביום אחד ונציע את הפתרון האופטימלי. הזמן הגדרת Superset עם מודול אבטחה מוכן וקבל ייעוץ על אינטגרציה עם כל framework.







