如何在PowerBI嵌入场景中自动生成Azure AD访问令牌?
我有一个React应用,借助powerbi-client-react库,采用**user-owns-data(又称embed-for-organization)**方式嵌入PowerBI服务报表,代码示例如下:
<PowerBIEmbed embedConfig = {{ type: "report", // Supported types: report, dashboard, tile, visual and qna id: "281839f6-4971-4ad3rtt", tokenType: models.TokenType.Aad, accessToken : "938orie90rekjd-9393" // ....some more properties here.... }} />
其中,models对象从powerbi-client库导入,<PowerBIEmbed/>来自依赖包powerbi-client-react。
目前我获取Azure AD访问令牌的方式是:
- 登录我的PowerBI账号
- 打开浏览器控制台,执行
copy(powerbiaccesstoken)获取令牌 - 将令牌粘贴到代码中
该令牌有效期仅1小时,之后嵌入的报表会弹出PowerBI登录提示,我需要重复上述操作重新获取并粘贴令牌。我拥有一个可用于生成访问令牌的专用PowerBI Pro账号,请问是否有办法无需重复手动复制令牌,通过前端或后端的JavaScript代码在令牌过期前自动完成生成操作?
后端自动生成令牌(推荐方案)
直接在前端处理账号凭据会导致信息泄露,生产环境必须采用后端生成令牌的方式,通过专用PowerBI Pro账号的凭据获取Azure AD令牌后,再提供给前端使用。以下是基于Node.js的实现示例:
步骤1:配置Azure AD应用
先在Azure AD中注册应用,授予它PowerBI Service的Delegated权限(如Report.Read.All、Dashboard.Read.All,按需选择),并获取以下信息:
- 客户端ID(Client ID)
- 客户端密钥(Client Secret)
- 租户ID(Tenant ID)
- 专用PowerBI Pro账号的用户名和密码
步骤2:Node.js后端代码示例
使用@azure/msal-node库实现令牌获取逻辑,并暴露接口供前端调用:
const { ConfidentialClientApplication } = require('@azure/msal-node'); const express = require('express'); const app = express(); // Azure AD配置参数 const msalConfig = { auth: { clientId: "YOUR_AZURE_AD_CLIENT_ID", authority: "https://login.microsoftonline.com/YOUR_TENANT_ID", clientSecret: "YOUR_AZURE_AD_CLIENT_SECRET" } }; const pca = new ConfidentialClientApplication(msalConfig); // 获取PowerBI访问令牌 async function getPowerBIEmbedToken() { const tokenRequest = { scopes: ["https://analysis.windows.net/powerbi/api/.default"], username: "YOUR_POWERBI_PRO_USERNAME", password: "YOUR_POWERBI_PRO_PASSWORD" }; try { const response = await pca.acquireTokenByUsernamePassword(tokenRequest); return response.accessToken; } catch (error) { console.error("令牌获取失败:", error); throw error; } } // 暴露接口给前端 app.get('/api/get-powerbi-token', async (req, res) => { try { const token = await getPowerBIEmbedToken(); res.json({ accessToken: token }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.listen(3001, () => console.log("后端服务运行在3001端口"));
步骤3:React前端调用接口并自动刷新令牌
在React应用中,初始化时获取令牌,并设置定时任务在令牌过期前10分钟刷新:
import { useState, useEffect } from 'react'; import { PowerBIEmbed } from 'powerbi-client-react'; import { models } from 'powerbi-client'; function PowerBIReport() { const [accessToken, setAccessToken] = useState(""); // 调用后端接口获取令牌 const fetchToken = async () => { const response = await fetch('http://localhost:3001/api/get-powerbi-token'); const data = await response.json(); setAccessToken(data.accessToken); }; // 初始化获取令牌,每50分钟刷新一次 useEffect(() => { fetchToken(); const refreshInterval = setInterval(fetchToken, 50 * 60 * 1000); return () => clearInterval(refreshInterval); }, []); const embedConfig = { type: "report", id: "281839f6-4971-4ad3rtt", tokenType: models.TokenType.Aad, accessToken: accessToken, // 其他配置项 }; return accessToken ? <PowerBIEmbed embedConfig={embedConfig} /> : <div>加载中...</div>; } export default PowerBIReport;
前端直接获取令牌(仅适合测试环境)
如果仅用于测试,可以使用@azure/msal-browser库在前端直接获取,但此方式会暴露账号凭据,绝对禁止用于生产环境:
import { PublicClientApplication } from '@azure/msal-browser'; import { useState, useEffect } from 'react'; import { PowerBIEmbed } from 'powerbi-client-react'; import { models } from 'powerbi-client'; const pca = new PublicClientApplication({ auth: { clientId: "YOUR_AZURE_AD_CLIENT_ID", authority: "https://login.microsoftonline.com/YOUR_TENANT_ID" } }); function PowerBIReport() { const [accessToken, setAccessToken] = useState(""); const fetchToken = async () => { try { const response = await pca.acquireTokenByUsernamePassword({ scopes: ["https://analysis.windows.net/powerbi/api/.default"], username: "YOUR_POWERBI_PRO_USERNAME", password: "YOUR_POWERBI_PRO_PASSWORD" }); setAccessToken(response.accessToken); } catch (err) { console.error(err); } }; useEffect(() => { fetchToken(); const refreshInterval = setInterval(fetchToken, 50 * 60 * 1000); return () => clearInterval(refreshInterval); }, []); const embedConfig = { type: "report", id: "281839f6-4971-4ad3rtt", tokenType: models.TokenType.Aad, accessToken: accessToken, // 其他配置项 }; return accessToken ? <PowerBIEmbed embedConfig={embedConfig} /> : <div>加载中...</div>; } export default PowerBIReport;
内容的提问来源于stack exchange,提问作者navinrangar

