使用簽署嵌入將 Looker 嵌入 iframe 時,部分瀏覽器預設會採用禁止第三方 Cookie 的 Cookie 政策。如果嵌入的 iframe 是從與載入嵌入應用程式的網域不同的網域載入,系統就會拒絕第三方 Cookie。一般來說,您可以要求並使用自訂網域來解決這項限制。不過,自訂網域無法用於某些情況。在這些情況下,您可以使用 Looker 無 Cookie 嵌入。
無 Cookie 嵌入如何運作?
如果未封鎖第三方 Cookie,使用者首次登入 Looker 時,系統會建立工作階段 Cookie。這項 Cookie 會隨每個使用者要求傳送,Looker 伺服器會使用這項 Cookie 建立發出要求的使用者身分。如果封鎖 Cookie,系統就不會隨要求傳送 Cookie,因此 Looker 伺服器無法識別與要求相關聯的使用者。
為解決這個問題,Looker 無 Cookie 嵌入功能會將權杖與每個要求建立關聯,以便在 Looker 伺服器中重新建立使用者工作階段。嵌入式應用程式有責任取得這些權杖,並提供給在嵌入式 iframe 中執行的 Looker 執行個體。本文件其餘部分會說明如何取得及提供這些權杖。
如要使用任一 API,嵌入應用程式必須能夠透過管理員權限驗證 Looker API。嵌入網域也必須列在「嵌入網域允許清單」中,或者,如果使用 Looker 23.8 以上版本,嵌入網域可以在取得無 Cookie 工作階段時一併加入。
建立 Looker 嵌入 iframe
下方的序列圖說明如何建立嵌入 iframe。系統可能會同時或在未來某個時間點產生多個 iframe。如果實作正確,iframe 會自動加入第一個 iframe 建立的工作階段。Looker Embed SDK 會自動加入現有工作階段,簡化這個程序。

- 使用者在嵌入應用程式中執行動作,導致系統建立 Looker iframe。
- 嵌入應用程式用戶端會取得 Looker 工作階段。您可以使用 Looker Embed SDK 啟動這個工作階段,但必須提供端點網址或回呼函式。如果使用回呼函式,系統會呼叫嵌入應用程式伺服器來取得 Looker 嵌入工作階段。否則,Embed SDK 會呼叫提供的端點網址。
- 嵌入應用程式伺服器會使用 Looker API 取得嵌入工作階段。這項 API 呼叫與 Looker 簽署的嵌入簽署程序類似,因為它會接受嵌入使用者定義做為輸入內容。如果呼叫使用者已有 Looker 嵌入工作階段,則應在呼叫中加入相關聯的工作階段參照權杖。本文的「取得工作階段」一節會詳細說明這項程序。
- 取得嵌入工作階段端點的處理方式與已簽署的
/login/embed/(signed url)端點類似,因為它會將 Looker 嵌入使用者定義視為要求主體,而非 URL。取得嵌入工作階段端點程序會驗證,然後建立或更新嵌入使用者。也可以接受現有的工作階段參照權杖。這點非常重要,因為這樣一來,多個 Looker 嵌入式 iframe 就能共用同一個工作階段。如果提供工作階段參照符記,且工作階段尚未過期,系統就不會更新嵌入使用者。這項功能支援的用途包括:使用已簽署的嵌入網址建立一個 iframe,並使用未簽署的嵌入網址建立其他 iframe。在這種情況下,沒有已簽署嵌入網址的 iframe 會沿用第一個工作階段的 Cookie。 - Looker API 呼叫會傳回四個權杖,每個權杖都有存留時間 (TTL):
- 授權權杖 (存留時間 = 30 秒)
- 導覽權杖 (TTL = 10 分鐘)
- API 權杖 (TTL = 10 分鐘)
- 工作階段參考符記 (TTL = 工作階段的剩餘生命週期)
- 嵌入應用程式伺服器必須追蹤 Looker 資料傳回的資料,並將其與呼叫使用者和呼叫使用者瀏覽器的使用者代理程式建立關聯。如需相關建議,請參閱本文的「產生權杖」一節。這項呼叫會傳回授權權杖、導覽權杖和 API 權杖,以及所有相關聯的 TTL。工作階段參照權杖應受到保護,且不得在呼叫瀏覽器中公開。
權杖傳回瀏覽器後,必須建構 Looker 嵌入登入網址。Looker 嵌入 SDK 會自動建構嵌入登入網址。如要使用
windows.postMessageAPI 建構嵌入登入網址,請參閱本文的「使用 Lookerwindows.postMessageAPI」一節中的範例。登入網址未包含已簽署的嵌入使用者詳細資料。其中包含目標 URI,包括導覽權杖和授權權杖 (以查詢參數形式)。授權權杖必須在 30 秒內使用,且只能使用一次。如需其他 iframe,必須再次取得嵌入工作階段。不過,如果提供工作階段參照權杖,授權權杖就會與相同工作階段建立關聯。
Looker 嵌入登入端點會判斷登入是否適用於無 Cookie 嵌入,這類登入會以授權權杖表示。如果授權權杖有效,系統會檢查下列項目:
- 相關聯的工作階段仍然有效。
- 相關聯的嵌入使用者仍有效。
- 與要求相關聯的瀏覽器使用者代理程式,與工作階段相關聯的瀏覽器代理程式相符。
如果上一個步驟的檢查通過,系統會使用網址中包含的目標 URI 重新導向要求。這與 Looker 簽署嵌入登入的程序相同。
這項要求是啟動 Looker 資訊主頁的重新導向。這項要求會將導覽權杖做為參數。
執行端點前,Looker 伺服器會先在要求中尋找導覽權杖。如果伺服器找到權杖,會檢查下列項目:
- 相關聯的工作階段仍然有效。
- 與要求相關聯的瀏覽器使用者代理程式,與工作階段相關聯的瀏覽器代理程式相符。
如果有效,系統會為要求還原工作階段,並執行資訊主頁要求。
系統會將載入資訊主頁的 HTML 傳回 iframe。
在 iframe 中執行的 Looker UI 會判斷資訊主頁 HTML 是無 Cookie 的嵌入回應。此時,Looker UI 會傳送訊息給嵌入應用程式,要求在步驟 6 中擷取的權杖。然後等待接收權杖。如果沒有收到權杖,系統會顯示訊息。
嵌入應用程式會將權杖傳送至 Looker 嵌入式 iframe。
收到權杖後,在 iframe 中執行的 Looker UI 會開始轉譯要求物件的程序。在此程序中,UI 會對 Looker 伺服器發出 API 呼叫。在步驟 15 中收到的 API 權杖會自動插入所有 API 要求中做為標頭。
執行任何端點前,Looker 伺服器會先在要求中尋找 API 權杖。如果伺服器找到權杖,會檢查下列項目:
- 相關聯的工作階段仍然有效。
- 與要求相關聯的瀏覽器使用者代理程式,與工作階段相關聯的瀏覽器代理程式相符。
如果工作階段有效,系統會還原要求的工作階段,並執行 API 要求。
系統會傳回資訊主頁資料。
系統會顯示資訊主頁。
使用者可以控管資訊主頁。
產生新權杖
下圖顯示產生新權杖的順序。

- 在嵌入式 iframe 中執行的 Looker UI 會監控嵌入權杖的 TTL。
- 權杖即將到期時,Looker UI 會將更新權杖訊息傳送至嵌入應用程式用戶端。
- 嵌入應用程式用戶端接著會向嵌入應用程式伺服器中實作的端點要求新權杖。Looker 嵌入 SDK 會自動要求新權杖,但必須提供端點網址或回呼函式。如果使用回呼函式,系統會呼叫嵌入應用程式伺服器來產生新權杖。否則,嵌入 SDK 會呼叫提供的端點網址。
- 嵌入應用程式會找出與嵌入工作階段相關聯的
session_reference_token。Looker Embed SDK Git 存放區提供的範例使用工作階段 Cookie,但您也可以使用分散式伺服器端快取 (例如 Redis)。 - 嵌入應用程式伺服器會呼叫 Looker 伺服器,要求產生權杖。除了發出要求的瀏覽器使用者代理程式外,這項要求也需要最近的 API 和導覽權杖。
- Looker 伺服器會驗證使用者代理程式、工作階段參照權杖、導覽權杖和 API 權杖。如果要求有效,系統就會產生新權杖。
- 權杖會傳回給呼叫的嵌入應用程式伺服器。
- 嵌入應用程式伺服器會從回應中移除工作階段參照權杖,並將其餘回應傳回給嵌入應用程式用戶端。
- 嵌入應用程式用戶端會將新產生的權杖傳送至 Looker UI。Looker Embed SDK 會自動執行這項操作。使用
windows.postMessageAPI 的嵌入應用程式用戶端則負責傳送權杖。Looker UI 收到權杖後,就會在後續的 API 呼叫和網頁導覽中使用這些權杖。
實作 Looker 無 Cookie 嵌入
如要導入 Looker 無 Cookie 嵌入功能,可以使用 Looker Embed SDK 或 windows.postMessage API。您可以使用 Looker Embed SDK 方法,但我們也提供如何使用 windows.postMessage API 的範例。如需這兩種實作方式的詳細說明,請參閱 Looker Embed SDK README 檔案。Embed SDK git 存放區也包含可運作的實作項目。
設定 Looker 執行個體
無 Cookie 嵌入與 Looker 簽署嵌入有許多相似之處。
必要條件
如要使用無 Cookie 嵌入功能並公開無 Cookie 嵌入 API 端點,Looker 管理員必須在 Looker「管理」面板中,驗證並啟用下列必要條件:
- 嵌入單一登入 (SSO) 驗證:在「管理」>「平台」>「嵌入」面板中,啟用「嵌入單一登入 (SSO) 驗證」。無 Cookie 嵌入功能以基礎設定為基礎,也就是底層的「嵌入單一登入驗證」函式。
- 持續性工作階段:在「管理員」>「驗證」>「工作階段」面板中,啟用「持續性工作階段」。
- API 使用者權限:請確認用於取得權杖的 API 憑證,屬於具有「管理員」角色或包含
manage_embed_settings權限的自訂 Looker 使用者。
設定 JSON Web Token 密鑰
與 Looker 簽署的嵌入不同,無 Cookie 嵌入不會使用「嵌入密碼」設定。無 Cookie 嵌入會使用 JSON Web Token (JWT),也就是「嵌入 JWT 密碼」設定,您可以在「管理」選單的「平台」部分,前往「嵌入」頁面設定或重設這項設定。
不需要設定 JWT 密鑰,因為系統會在首次嘗試建立不含 Cookie 的嵌入工作階段時建立 JWT。請避免重設這個權杖,否則所有有效的不含 Cookie 嵌入工作階段都會失效。
與嵌入密鑰不同,嵌入 JWT 密鑰不會公開,因為這項密鑰只會在 Looker 伺服器內部使用。
應用程式用戶端實作
本節包含如何在應用程式用戶端中導入無 Cookie 嵌入功能的範例,並包含下列小節:
安裝或更新 Looker Embed SDK
如要使用無 Cookie 嵌入功能,必須使用下列 Looker SDK 版本:
@looker/embed-sdk >= 2.0.0
@looker/sdk >= 22.16.0
使用 Looker Embed SDK
Embed SDK 新增了初始化方法,可啟動無 Cookie 工作階段。這個方法接受兩個網址字串或兩個回呼函式。網址字串應參照嵌入應用程式伺服器中的端點。應用程式伺服器上這些端點的實作詳細資料,請參閱本文的「應用程式伺服器實作」一節。
getEmbedSDK().initCookieless(
runtimeConfig.lookerHost,
'/acquire-embed-session',
'/generate-embed-tokens'
)
以下範例說明如何使用回呼。只有在嵌入用戶端應用程式需要瞭解 Looker 嵌入工作階段的狀態時,才應使用回呼。您也可以使用 session:status 事件,這樣就不必搭配 Embed SDK 使用回呼。
const acquireEmbedSessionCallback =
async (): Promise<LookerEmbedCookielessSessionData> => {
const resp = await fetch('/acquire-embed-session')
if (!resp.ok) {
console.error('acquire-embed-session failed', { resp })
throw new Error(
`acquire-embed-session failed: ${resp.status} ${resp.statusText}`
)
}
return (await resp.json()) as LookerEmbedCookielessSessionData
}
const generateEmbedTokensCallback =
async ({ api_token, navigation_token }): Promise<LookerEmbedCookielessSessionData> => {
const resp = await fetch('/generate-embed-tokens', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ api_token, navigation_token }),
})
if (!resp.ok) {
console.error('generate-embed-tokens failed', { resp })
throw new Error(
`generate-embed-tokens failed: ${resp.status} ${resp.statusText}`
)
}
return (await resp.json()) as LookerEmbedCookielessSessionData
}
getEmbedSDK().initCookieless(
runtimeConfig.lookerHost,
acquireEmbedSessionCallback,
generateEmbedTokensCallback
)
使用 Looker windows.postMessage API
如要查看使用 windows.postMessage API 的詳細範例,請參閱 Embed SDK Git 存放區中的 message_example.ts 和 message_utils.ts 檔案。如要瞭解範例的重點,請參閱這篇文章。
以下範例說明如何建構 iframe 的網址。回呼函式與先前看到的 acquireEmbedSessionCallback 範例相同。
private async getCookielessLoginUrl(): Promise<string> {
const { authentication_token, navigation_token } =
await this.embedEnvironment.acquireSession()
const url = this.embedUrl.startsWith('/embed')
? this.embedUrl
: `/embed${this.embedUrl}`
const embedUrl = new URL(url, this.frameOrigin)
if (!embedUrl.searchParams.has('embed_domain')) {
embedUrl.searchParams.set('embed_domain', window.location.origin)
}
embedUrl.searchParams.set('embed_navigation_token', navigation_token)
const targetUri = encodeURIComponent(
`${embedUrl.pathname}${embedUrl.search}${embedUrl.hash}`
)
return `${embedUrl.origin}/login/embed/${targetUri}?embed_authentication_token=${authentication_token}`
}
以下範例說明如何監聽權杖要求、產生新權杖,並將權杖傳送至 Looker。回呼函式與先前的 generateEmbedTokensCallback 範例相同。
this.on(
'session:tokens:request',
this.sessionTokensRequestHandler.bind(this)
)
private connected = false
private async sessionTokensRequestHandler(_data: any) {
const contentWindow = this.getContentWindow()
if (contentWindow) {
if (!this.connected) {
// When not connected the newly acquired tokens can be used.
const sessionTokens = this.embedEnvironment.applicationTokens
if (sessionTokens) {
this.connected = true
this.send('session:tokens', this.embedEnvironment.applicationTokens)
}
} else {
// If connected, the embedded Looker application has decided that
// it needs new tokens. Generate new tokens.
const sessionTokens = await this.embedEnvironment.generateTokens()
this.send('session:tokens', sessionTokens)
}
}
}
send(messageType: string, data: any = {}) {
const contentWindow = this.getContentWindow()
if (contentWindow) {
const message: any = {
type: messageType,
...data,
}
contentWindow.postMessage(JSON.stringify(message), this.frameOrigin)
}
return this
}
實作應用程式伺服器
本節包含如何在應用程式伺服器中實作無 Cookie 嵌入的範例,並包含下列小節:
基本實作方式
嵌入應用程式必須實作兩個伺服器端點,用來叫用 Looker 端點。這是為了確保工作階段參考權杖安全無虞。這些端點包括:
- 取得工作階段:如果工作階段參照符記已存在且仍有效,工作階段要求會加入現有工作階段。建立 iframe 時會呼叫「取得工作階段」。
- 產生權杖 - Looker 會定期觸發對這個端點的呼叫。
取得工作階段
這個 TypeScript 範例會使用工作階段儲存或還原工作階段參考權杖。端點不一定要以 TypeScript 實作。
app.get(
'/acquire-embed-session',
async function (req: Request, res: Response) {
try {
const current_session_reference_token =
req.session && req.session.session_reference_token
const response = await acquireEmbedSession(
req.headers['user-agent']!,
user,
current_session_reference_token
)
const {
authentication_token,
authentication_token_ttl,
navigation_token,
navigation_token_ttl,
session_reference_token,
session_reference_token_ttl,
api_token,
api_token_ttl,
} = response
req.session!.session_reference_token = session_reference_token
res.json({
api_token,
api_token_ttl,
authentication_token,
authentication_token_ttl,
navigation_token,
navigation_token_ttl,
session_reference_token_ttl,
})
} catch (err: any) {
res.status(400).send({ message: err.message })
}
}
)
async function acquireEmbedSession(
userAgent: string,
user: LookerEmbedUser,
session_reference_token: string
) {
await acquireLookerSession()
try {
const request = {
...user,
session_reference_token: session_reference_token,
}
const sdk = new Looker40SDK(lookerSession)
const response = await sdk.ok(
sdk.acquire_embed_cookieless_session(request, {
headers: {
'User-Agent': userAgent,
},
})
)
return response
} catch (error) {
console.error('embed session acquire failed', { error })
throw error
}
}
從 Looker 23.8 開始,取得無 Cookie 工作階段時,可以加入嵌入網域。您也可以使用 Looker 「管理」>「嵌入」面板新增嵌入網域。Looker 會將嵌入網域儲存在 Looker 內部資料庫中,因此不會顯示在「管理」>「嵌入」面板上。嵌入網域會改為與無 Cookie 工作階段建立關聯,且只會在工作階段期間存在。如果您決定使用這項功能,請詳閱安全性最佳做法。
產生權杖
這個 TypeScript 範例會使用工作階段儲存或還原工作階段參考權杖。端點不一定要以 TypeScript 實作。
請務必瞭解如何處理無效權杖造成的 400 回應。雖然不應傳回 400 回應,但如果發生這種情況,最佳做法是終止 Looker 嵌入工作階段。如要終止 Looker 嵌入工作階段,可以毀損嵌入 iframe,或在 session:tokens 訊息中將 session_reference_token_ttl 值設為零。如果將 session_reference_token_ttl 值設為零,Looker iframe 會顯示工作階段過期對話方塊。
嵌入工作階段過期時,系統不會傳回 400 回應。如果嵌入工作階段已過期,系統會傳回 200 回應,並將 session_reference_token_ttl 值設為零。
app.put(
'/generate-embed-tokens',
async function (req: Request, res: Response) {
try {
const session_reference_token = req.session!.session_reference_token
const { api_token, navigation_token } = req.body as any
const tokens = await generateEmbedTokens(
req.headers['user-agent']!,
session_reference_token,
api_token,
navigation_token
)
res.json(tokens)
} catch (err: any) {
res.status(400).send({ message: err.message })
}
}
)
}
async function generateEmbedTokens(
userAgent: string,
session_reference_token: string,
api_token: string,
navigation_token: string
) {
if (!session_reference_token) {
console.error('embed session generate tokens failed')
// missing session reference treat as expired session
return {
session_reference_token_ttl: 0,
}
}
await acquireLookerSession()
try {
const sdk = new Looker40SDK(lookerSession)
const response = await sdk.ok(
sdk.generate_tokens_for_cookieless_session(
{
api_token,
navigation_token,
session_reference_token: session_reference_token || '',
},
{
headers: {
'User-Agent': userAgent,
},
}
)
)
return {
api_token: response.api_token,
api_token_ttl: response.api_token_ttl,
navigation_token: response.navigation_token,
navigation_token_ttl: response.navigation_token_ttl,
session_reference_token_ttl: response.session_reference_token_ttl,
}
} catch (error: any) {
if (error.message?.includes('Invalid input tokens provided')) {
// The Looker UI does not know how to handle bad
// tokens. This shouldn't happen but if it does expire the
// session. If the token is bad there is not much that that
// the Looker UI can do.
return {
session_reference_token_ttl: 0,
}
}
console.error('embed session generate tokens failed', { error })
throw error
}
實作的考量
嵌入應用程式必須追蹤工作階段參照權杖,並確保權杖安全無虞。這個權杖應與嵌入應用程式使用者建立關聯。嵌入應用程式權杖可透過下列其中一種方式儲存:
- 在內嵌應用程式使用者工作階段中
- 在叢集環境中可用的伺服器端快取中
- 在與使用者相關聯的資料庫表格中
如果工作階段是以 Cookie 形式儲存,Cookie 應經過加密。嵌入 SDK 存放區中的範例會使用工作階段 Cookie 儲存工作階段參考權杖。
Looker 嵌入工作階段到期時,嵌入的 iframe 會顯示對話方塊。此時,使用者無法在嵌入的例項中執行任何動作。發生這種情況時,系統會產生 session:status 事件,讓嵌入應用程式偵測嵌入 Looker 應用程式的目前狀態,並採取某種動作。
嵌入應用程式可以檢查 generate_tokens 端點傳回的 session_reference_token_ttl 值是否為零,藉此偵測嵌入工作階段是否已過期。如果值為零,表示嵌入工作階段已過期。建議在初始化無 Cookie 嵌入時,使用回呼函式產生權杖。回呼函式接著可以判斷嵌入工作階段是否已過期,並毀損嵌入的 iframe,做為使用預設嵌入工作階段過期對話方塊的替代方案。
執行 Looker 無 Cookie 嵌入範例
嵌入 SDK 存放區包含以 TypeScript 編寫的節點 Express 伺服器和用戶端,可實作嵌入應用程式。先前顯示的範例就是取自這項實作。以下假設您的 Looker 執行個體已設定為使用無 Cookie 嵌入功能,如先前所述。
您可以按照下列方式執行伺服器:
- 複製 Embed SDK 存放區 -
git clone git@github.com:looker-open-source/embed-sdk.git - 變更目錄 -
cd embed-sdk - 安裝依附元件 -
npm install - 按照本文「設定伺服器」一節的說明設定伺服器。
- 執行伺服器 -
npm run server
設定伺服器
在複製的存放區根目錄中建立 .env 檔案 (這會包含在 .gitignore 中)。
格式如下:
LOOKER_WEB_URL=your-looker-instance-url.com
LOOKER_API_URL=https://your-looker-instance-url.com
LOOKER_DEMO_HOST=localhost
LOOKER_DEMO_PORT=8080
LOOKER_EMBED_SECRET=embed-secret-from-embed-admin-page
LOOKER_CLIENT_ID=client-id-from-user-admin-page
LOOKER_CLIENT_SECRET=client-secret-from-user-admin-page
LOOKER_DASHBOARD_ID=id-of-dashboard
LOOKER_LOOK_ID=id-of-look
LOOKER_EXPLORE_ID=id-of-explore
LOOKER_EXTENSION_ID=id-of-extension
LOOKER_VERIFY_SSL=true
LOOKER_REPORT_ID=id-of-report
LOOKER_QUERY_VISUALIZATION_ID=id-of-query-visualization
在新分頁中開啟連結時,排解 401 Unauthorized 錯誤
使用 Looker 無 Cookie 嵌入功能時,如果使用者嘗試在新瀏覽器分頁或視窗中開啟連結 (例如資訊主頁向下鑽取、探索或文件下載),可能會遇到 HTTP 401 Unauthorized 錯誤,或重新導向至 Looker 登入頁面。如果瀏覽器停用第三方 Cookie,最常會發生這個問題。
原因
為防範工作階段劫持漏洞,Looker (版本 25.18.61 以上、25.20.44 以上和 26.0.22 以上) 導入了安全性修補程式,刻意停止在初始無 Cookie 嵌入回應中傳回工作階段 Cookie。由於瀏覽器不再收到這個工作階段 Cookie,在新分頁或視窗中開啟 Looker 網址時,驗證環境會遺失,導致要求失敗。
解決方法和替代方案
- 升級 Looker:將 Looker 執行個體升級至 26.0.36 以上版本。這個版本導入修正程式,可在無 Cookie 嵌入工作階段期間,安全地將一次性權杖新增至在新視窗中開啟的網址,讓使用者能夠在新分頁中開啟安全連結。
- 在同一個 iframe 中開啟連結:如果無法立即升級,請將連結目標設為
_self(例如target="_self"),在同一個 iframe 中開啟內嵌資訊主頁中的所有連結。 - 透過主機應用程式傳送:如果必須在新分頁或視窗中開啟,請將連結導向主機應用程式中的新網頁,初始化新的無 Cookie 嵌入 iframe,而非直接連結至 Looker 執行個體。
- 使用自訂網域搭配簽署嵌入:您也可以考慮使用標準簽署嵌入搭配自訂網域。