如何为嵌入的Superset仪表板实现Refresh Token功能?
Superset嵌入仪表板的Refresh Token实现方案
核心思路
要解决Access Token过期导致Guest Token失效的问题,需要复用Refresh Token自动刷新Access Token,并让前端能动态获取新的Guest Token,而非仅依赖初始生成的令牌。
步骤1:存储Refresh Token与过期时间
登录Superset获取令牌时,除了access_token,还要保存refresh_token和过期时间expires_in(单位秒),留10分钟缓冲避免提前过期:
$dataLoginToken = $this->curlRequest($urlLogin, ['Content-Type:application/json'], 'POST', $payloadLogin); // 存储令牌信息到session $session = $this->get('session'); $session->set('superset_access_token', $dataLoginToken['access_token']); $session->set('superset_refresh_token', $dataLoginToken['refresh_token']); // 计算过期时间:当前时间 + 有效期 - 10分钟缓冲 $expiresAt = time() + $dataLoginToken['expires_in'] - 600; $session->set('superset_access_token_expires_at', $expiresAt);
步骤2:实现Access Token刷新方法
新增方法,用Refresh Token获取新的Access Token,并更新session存储:
private function refreshSupersetAccessToken(): array { $baseUrl = 'http://supersetdomain.com:8088/'; $session = $this->get('session'); $refreshToken = $session->get('superset_refresh_token'); $urlRefresh = $baseUrl . 'api/v1/security/refresh'; $payloadRefresh = [ 'refresh_token' => $refreshToken ]; $dataRefresh = $this->curlRequest($urlRefresh, ['Content-Type:application/json'], 'POST', $payloadRefresh); // 更新session中的令牌信息 $session->set('superset_access_token', $dataRefresh['access_token']); $session->set('superset_refresh_token', $dataRefresh['refresh_token']); $expiresAt = time() + $dataRefresh['expires_in'] - 600; $session->set('superset_access_token_expires_at', $expiresAt); return $dataRefresh; }
步骤3:修改Guest Token生成逻辑,自动刷新Access Token
在生成Guest Token前,先检查Access Token是否过期,过期则自动刷新:
public function generateTokenAction(Request $request, $id = null): Response { $baseUrl = 'http://supersetdomain.com:8088/'; $session = $this->get('session'); // 检查Access Token是否过期 $accessToken = $session->get('superset_access_token'); $expiresAt = $session->get('superset_access_token_expires_at'); if (time() >= $expiresAt) { // 刷新Access Token $this->refreshSupersetAccessToken(); $accessToken = $session->get('superset_access_token'); } // 后续获取CSRF、生成Guest Token的逻辑不变,使用更新后的accessToken $dashboard = $supersetDashboardRepo->find($id); $dashboard_embeddedCode = $dashboard->getEmbeddedCode(); $urlCsrf = $baseUrl . 'api/v1/security/csrf_token/'; $dataCsrfToken = $this->curlRequest($urlCsrf, ['Authorization: Bearer ' . $accessToken]); $csrfToken = $dataCsrfToken['result']; $urlGuestToken = $baseUrl . 'api/v1/security/guest_token/'; $payloadGuestToken= [ 'user' => [ 'username' => username, 'first_name' => first_name, 'last_name' => last_name, ], "resources" => [ [ "type" => "dashboard", "id" => $dashboard_embeddedCode ] ], "rls" => [] ]; $headersGuestToken = [ 'X-CSRF-Token: '. $csrfToken, 'Content-Type: application/json', 'Authorization: Bearer ' . $accessToken ]; $dataGuestToken = $this->curlRequest($urlGuestToken, $headersGuestToken, 'POST', $payloadGuestToken); $guestToken = $dataGuestToken['token']; return new Response(json_encode([ 'embeddedCode' => $dashboard_embeddedCode, 'guest_token' => $guestToken ])); }
步骤4:前端动态获取Guest Token
将fetchGuestToken改为动态调用后端接口,SDK会在令牌过期时自动触发该方法获取新令牌:
supersetEmbeddedSdk.embedDashboard({ id: data.embeddedCode, supersetDomain: "supersetdomain.com:8088/", mountPoint: containerDashboard, // 动态调用后端接口获取最新Guest Token fetchGuestToken: async () => { const response = await fetch(`/guest_token/${dashboardId}`, { method: 'GET', credentials: 'include' // 携带session信息,后端可读取存储的Refresh Token }); const result = await response.json(); return result.guest_token; }, dashboardUiConfig: {} })
额外注意事项
- 分布式部署适配:多服务器环境下,建议用Redis等分布式存储替代默认session,避免令牌信息不一致。
- Refresh Token过期处理:若Refresh Token也过期,后端需返回特定状态码,前端引导用户重新加载页面触发登录流程。
- 错误捕获:在令牌刷新和生成逻辑中添加异常捕获,处理网络错误、令牌无效等场景。
内容的提问来源于stack exchange,提问作者El Simple
相关产品推荐
相关产品推荐

