PowerBI 和 Azure AD 无头登录

PowerBI and Azure AD Headless Login

我正在尝试将 PowerBI 仪表板嵌入到我的客户 MVC 门户中。我的客户没有AAD账号,所以他们来网站的时候不能登录Live,他们是用个人权限登录我的MVC网站。

我已经在 PowerBI/AAD 上注册了我的应用程序并拥有 ClientID 和 Secret。我调用 AAD 并获得一个授权码,然后我用它来获得一个成功返回的认证令牌。

每当我使用访问令牌获取仪表板时,它都会被 403 Forbidden 不断拒绝。

我已经查看了 Microsoft 的所有示例,但它们需要用户登录提示。我查看了引用 AcquireToken 方法的 ADAL2.0 代码,但这在 ADAL3 中已弃用并替换为具有不同参数的 AcquireTokenAsync,我在下面的示例中使用了它。

获取令牌的函数如下:

    protected AuthenticationResult GetAccessToken()
    {
        string pBiUser = Properties.Settings.Default.PowerBIUser;
        string pBiPwd = Properties.Settings.Default.PowerBIPwd;
        string pBiClientId = Properties.Settings.Default.PowerBIClientId;
        string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
        TokenCache TC = new TokenCache();
        ClientCredential CC = new ClientCredential(pBiClientId,pBiSecret);
        string AU = Properties.Settings.Default.PowerBIAuthority;
        Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authenticationContext 
            = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AU, TC);
        AuthenticationResult result = authenticationContext.AcquireTokenAsync("https://analysis.windows.net/powerbi/api"
            ,CC).Result;

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the PowerBI token");
        }

        return result;
    }

然后我拿到结果令牌并调用。 响应 收到 403:

    protected PBIDashboards GetDashboards(AuthenticationResult authResult)
    {
        PBIDashboards pbiDashboards = new PBIDashboards();
        var baseAddress = new Uri("https://api.powerbi.com");
        using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
        {
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
                "Bearer " + authResult.AccessToken);
            using (**var response** = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
            {
                string responseData = response.Content.ReadAsStringAsync().Result;

                //Deserialize JSON string
                pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);

                if (pbiDashboards != null)
                {
                    var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
                    {
                        Id = dashboard.id,
                        DisplayName = dashboard.displayName,
                        EmbedUrl = dashboard.embedUrl
                    });
                }
            }
        }
        return pbiDashboards;
    }

根据错误信息(403),问题与权限有关。

据我所知,当我们使用 Power BI REST 的 客户端凭据流 获取访问令牌时,我们无法使用此类权限。可以参考下图的权限:

要在无需用户交互的情况下获取 Power BI REST 的令牌,我们可以使用资源所有者密码凭据流程。您可以使用已经实现此功能的第 3 方库 PowerBI.Api.Client

经过大量研究,您可以直接AJAX调用获取令牌:

    private async Task<string> GetAccessToken()
    {
        string pBiUser = Properties.Settings.Default.PowerBIUser;
        string pBiPwd = Properties.Settings.Default.PowerBIPwd;
        string pBiClientId = Properties.Settings.Default.PowerBIClientId;
        string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
        string pBITenant = Properties.Settings.Default.PowerBITenantId;

        string tokenEndpointUri = "https://login.microsoftonline.com/"+pBITenant+"/oauth2/token";

        var content = new FormUrlEncodedContent(new[]
            {
        new KeyValuePair<string, string>("grant_type", "password"),
        new KeyValuePair<string, string>("username", pBiUser),
        new KeyValuePair<string, string>("password", pBiPwd),
        new KeyValuePair<string, string>("client_id", pBiClientId),
        new KeyValuePair<string, string>("client_secret", pBiSecret),
        new KeyValuePair<string, string>("resource", "https://analysis.windows.net/powerbi/api")
        });

        using (var client = new HttpClient())
        {
            HttpResponseMessage res = client.PostAsync(tokenEndpointUri, content).Result;

            string json = await res.Content.ReadAsStringAsync();

            AzureAdTokenResponse tokenRes = JsonConvert.DeserializeObject<AzureAdTokenResponse>(json);

            return tokenRes.AccessToken;
        }
    }

获得字符串 AccessToken 后,即可调用 Dashboards 请求。

protected PBIDashboards GetDashboards(string token)
    {
        PBIDashboards pbiDashboards = new PBIDashboards();
        var baseAddress = new Uri("https://api.powerbi.com");
        using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
        {
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
                "Bearer " + token);
            using (var response = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
            {
                string responseData = response.Content.ReadAsStringAsync().Result;

                //Deserialize JSON string
                pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);

                if (pbiDashboards != null)
                {
                    var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
                    {
                        Id = dashboard.id,
                        DisplayName = dashboard.displayName,
                        EmbedUrl = dashboard.embedUrl
                    });
                }
            }
        }
        return pbiDashboards;
    }

这将为您提供仪表板列表和仪表板 ID,以调用 PowerBI API 在 Javascript 中构建嵌入页面。我使用隐藏的输入字段来存储访问令牌并嵌入 URL 以传递给 Javascript 调用。

// check if the embed url was selected
var embedUrl = document.getElementById('embed').value;
if (embedUrl === "")
    return;

// get the access token.
accessToken = document.getElementById('token').value;

// Embed configuration used to describe the what and how to embed.
// This object is used when calling powerbi.embed.
// You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
var config = {
    type: 'dashboard',
    accessToken: accessToken,
    embedUrl: embedUrl
};

// Grab the reference to the div HTML element that will host the dashboard.
var dashboardContainer = document.getElementById('dashboard');

// Embed the dashboard and display it within the div container.
var dashboard = powerbi.embed(dashboardContainer, config);