处理身份验证

身份验证类型

扩展支持一种或多种身份验证。 每种身份验证类型都是不同类型的凭据。 Power Query 中向最终用户显示的身份验证 UI 由扩展支持的凭据类型驱动。

支持的身份验证类型列表定义为扩展的数据源类型 定义的一部分。 每个身份验证值都是具有特定字段的记录。 下表列出了每种类型的预期字段。 除非另有说明,否则所有字段都是必填字段。

身份验证类型 字段 说明
匿名 匿名(也称为Implicit)身份验证类型没有任何字段。
OAuth StartLogin 该函数提供用于启动 OAuth 流的 URL 和状态信息。

转到实现 OAuth 流部分。
FinishLogin 该函数用于提取访问令牌和其他与 OAuth 流相关的属性。
刷新 (可选)函数从刷新令牌检索新访问令牌。
Logout (可选)该函数使用户当前访问令牌失效。
Label (可选)一个文本值,允许覆盖此 AuthenticationKind 的默认标签。
Aad AuthorizationUri text 值或一元函数,返回 Microsoft Entra ID 授权终结点(示例:"https://login.microsoftonline.com/common/oauth2/authorize")。

转到“Microsoft Entra ID 身份验证”部分。
资源 text 值或一元函数,返回服务的 Microsoft Entra ID 资源值。
范围 (可选)text 值或一元函数,作为身份验证流的一部分返回要请求的范围列表。 应以空格分隔多个范围值。 范围值应为范围名称,不带应用程序 ID URI(示例:Data.Read)。 如果未提供,则会请求 user_impersonation 范围。
UsernamePassword UsernameLabel (可选)一个文本值,用于替换凭证 UI 上用户名的默认标签。
PasswordLabel (可选)一个文本值,用于替换凭证 UI 上密码的默认标签。
Label (可选)一个文本值,允许覆盖此 AuthenticationKind 的默认标签。
Windows UsernameLabel (可选)一个文本值,用于替换凭证 UI 上用户名的默认标签。
PasswordLabel (可选)一个文本值,用于替换凭证 UI 上密码的默认标签。
Label (可选)一个文本值,允许覆盖此 AuthenticationKind 的默认标签。
密钥 KeyLabel (可选)一个文本值,用于替换凭证 UI 上 API 密钥文本框的默认标签。
Label (可选)一个文本值,允许覆盖此 AuthenticationKind 的默认标签。

以下示例显示了支持 OAuth、密钥、Windows、基本(用户名和密码)和匿名凭证的连接器的身份验证记录。

示例:

Authentication = [
    OAuth = [
        StartLogin = StartLogin,
        FinishLogin = FinishLogin,
        Refresh = Refresh,
        Logout = Logout
    ],
    Key = [],
    UsernamePassword = [],
    Windows = [],
    Anonymous = []
]

访问当前凭证

可以使用Extension.CurrentCredential函数检索当前凭证。

已启用可扩展性的 M 数据源函数会自动继承扩展的凭据范围。 在大多数情况下,无需显式访问当前凭证,但是也有例外,例如:

  • 在自定义标头或查询字符串参数中传入凭据(例如,使用 API 密钥身份验证类型时)。
  • 为 ODBC 或 ADO.NET 扩展设置连接字符串属性。
  • 检查 OAuth 令牌上的自定义属性。
  • 使用凭证作为 OAuth v1 流的一部分。

Extension.CurrentCredential函数返回记录对象。 包含的字段特定于身份验证类型。 下表包含详细信息。

字段 说明 使用者
AuthenticationKind 包含分配给此凭据的身份验证类型的名称(UsernamePassword、OAuth 等)。 全部
用户名 用户名值 UsernamePassword、Windows
密码 密码值。 通常与 UsernamePassword 一起使用,但也针对密钥进行设置。 Key、UsernamePassword、Windows
access_token OAuth 访问令牌值。 OAuth
属性 包含给定凭据的其他自定义属性的记录。 通常与 OAuth 一起使用,以存储在身份验证流期间随访问令牌返回的其他属性(如刷新令牌)。 OAuth
密钥 API 密钥值。 请注意:密钥值也可以在“密码”字段中使用。 默认情况下,糅合引擎在 Authorization 标头中插入此密钥,就好像此值是基本身份验证密码(没有用户名)。 如果此类行为不是所需行为,则必须在选项记录中指定 ManualCredentials = true 选项。 密钥
EncryptConnection 一个逻辑值,确定是否需要与数据源建立加密连接。 此值可用于所有身份验证类型,但仅在数据源定义中指定 EncryptConnection 时才进行设置。 全部

下面的代码示例访问 API 密钥的当前凭据,并使用它填充自定义标头 (x-APIKey)。

示例:

MyConnector.Raw = (_url as text) as binary =>
let
    apiKey = Extension.CurrentCredential()[Key],
    headers = [

        #"x-APIKey" = apiKey,
        Accept = "application/vnd.api+json",
        #"Content-Type" = "application/json"
    ],
    request = Web.Contents(_url, [ Headers = headers, ManualCredentials = true ])
in
    request

实现 OAuth 流

OAuth 身份验证类型允许扩展为其服务实现自定义逻辑。 为此,扩展提供用于StartLogin (返回启动 OAuth 流的授权 URI)和 FinishLogin (将授权代码交换为访问令牌)的函数。 扩展还可以选择性地实现 Refresh(将刷新令牌交换为新访问令牌)和 Logout(过期当前刷新令牌和访问令牌)函数。

注意

Power Query 扩展在客户端计算机上运行的应用程序中进行评估。 数据连接器不应在其 OAuth 流中使用机密秘密,因为用户可以检查扩展或网络流量来获知秘密。 有关提供不依赖于共享密钥的流的详细信息,请转到 OAuth 公共客户端的代码交换证明密钥 RFC(也称为 PKCE)。 可以在我们的 GitHub 网站上找到此流的示例实现。

有两组 OAuth 函数签名:包含最少数量参数的原始签名,以及接受更多参数的高级签名。 大多数 OAuth 流都可以使用原始签名实现。 还可以在实现中混搭使用签名类型。 函数调用是基于参数数量(及其类型)的匹配项。 不考虑参数名称。

转到 GitHub 示例以获取更多详细信息。

原始 OAuth 签名

StartLogin = (dataSourcePath, state, display) => ...;

FinishLogin = (context, callbackUri, state) => ...;

Refresh = (dataSourcePath, refreshToken) =>  ...;

Logout = (accessToken) => ...;

高级 OAuth 签名

有关高级签名的说明:

  • 所有签名都接受一个clientApplication记录值,该值保留以供将来使用。
  • 所有签名都接受一个dataSourcePath(大多数示例中也称为resourceUrl)。
  • Refresh函数接受 oldCredential参数,该参数是FinishLogin函数返回的之前record(或之前对Refresh的调用)。
StartLogin = (clientApplication, dataSourcePath, state, display) => ...;

FinishLogin = (clientApplication, dataSourcePath, context, callbackUri, state) => ...;

Refresh = (clientApplication, dataSourcePath, oldCredential) =>  ...;

Logout = (clientApplication, dataSourcePath, accessToken) => ...;

Microsoft Entra ID 身份验证

Aad 身份验证类型是适用于 Microsoft Entra ID 的 OAuth 的专用版本。 其使用与支持机构帐户身份验证的内置 Power Query 连接器相同的 Microsoft Entra ID 客户端。 有关详细信息,请参阅为自定义连接器配置 Microsoft Entra 快速入门指南。

注意

如果为 Microsoft Entra ID 实现自己的 OAuth 流,则为租户启用条件访问的用户在使用 Power BI 服务刷新时可能会遇到问题。 这不会影响基于网关的刷新,但会影响支持从 Power BI 服务刷新的认证连接器。 通过 Power BI 服务配置基于 Web 的凭证时,用户可能会遇到由使用公共客户端应用程序的连接器引发的问题。 此流生成的访问令牌最终将用于另一台计算机(即 Azure 数据中心中的 Power BI 服务,而不是公司网络上),而不是最初进行身份验证的计算机(即在公司网络上配置数据源凭证的用户的计算机)。 内置 Aad 类型通过在 Power BI 服务中配置凭证时使用不同的 Microsoft Entra ID 客户端来解决此问题。 此选项不适用于使用OAuth身份验证类型的连接器。

大多数连接器都需要为AuthorizationUriResource字段提供值。 这两个字段可以是text值,也可以是返回text value的单个参数函数。

AuthorizationUri = "https://login.microsoftonline.com/common/oauth2/authorize"
AuthorizationUri = (dataSourcePath) => FunctionThatDeterminesAadEndpointFromDataSourcePath(dataSourcePath)
Resource = "77256ee0-fe79-11ea-adc1-0242ac120002"   // Microsoft Entra ID resource value for your service - Guid or URL
Resource = (dataSourcePath) => FunctionThatDeterminesResourceFromDataSourcePath(dataSourcePath)

使用基于 URI 的标识符的连接器不需要提供Resource值。 默认情况下,该值等于连接器 URI 参数的根路径。 如果数据源的 Microsoft Entra ID 资源不同于域值(例如,它使用 GUID),则需要提供一个 Resource 值。

Aad 身份验证类型示例

在以下情况下,数据源支持使用通用租户(无 Azure B2B 支持)的全局云 Microsoft Entra ID。 请求 .default 范围 将返回一个令牌,其中包含 Power Query 客户端应用程序 ID 所有以前授权的范围。

Authentication = [
    Aad = [
        AuthorizationUri = "https://login.microsoftonline.com/common/oauth2/authorize",
        Resource = "77256ee0-fe79-11ea-adc1-0242ac120002", // Entra Application ID URI or app guid
        Scope = ".default"
    ]
]

在以下情况下,数据源支持基于 OpenID Connect (OIDC) 或类似协议的租户发现。 此功能允许连接器根据数据源路径中的一个或多个参数,确定要使用的正确 Microsoft Entra ID 终结点。 此动态发现方法允许连接器支持 Azure B2B。


// Implement this function to retrieve or calculate the service URL based on the data source path parameters
GetServiceRootFromDataSourcePath = (dataSourcePath) as text => ...;

GetAuthorizationUrlFromWwwAuthenticate = (url as text) as text =>
    let
        // Sending an unauthenticated request to the service returns
        // a 302 status with WWW-Authenticate header in the response. The value will
        // contain the correct authorization_uri.
        // 
        // Example:
        // Bearer authorization_uri="https://login.microsoftonline.com/{tenant_guid}/oauth2/authorize"
        responseCodes = {302, 401},
        endpointResponse = Web.Contents(url, [
            ManualCredentials = true,
            ManualStatusHandling = responseCodes
        ])
    in
        if (List.Contains(responseCodes, Value.Metadata(endpointResponse)[Response.Status]?)) then
            let
                headers = Record.FieldOrDefault(Value.Metadata(endpointResponse), "Headers", []),
                wwwAuthenticate = Record.FieldOrDefault(headers, "WWW-Authenticate", ""),
                split = Text.Split(Text.Trim(wwwAuthenticate), " "),
                authorizationUri = List.First(List.Select(split, each Text.Contains(_, "authorization_uri=")), null)
            in
                if (authorizationUri <> null) then
                    // Trim and replace the double quotes inserted before the url
                    Text.Replace(Text.Trim(Text.Trim(Text.AfterDelimiter(authorizationUri, "=")), ","), """", "")
                else
                    error Error.Record("DataSource.Error", "Unexpected WWW-Authenticate header format or value during authentication.", [
                        #"WWW-Authenticate" = wwwAuthenticate
                    ])
        else
            error Error.Unexpected("Unexpected response from server during authentication.");

<... snip ...>

Authentication = [
    Aad = [
        AuthorizationUri = (dataSourcePath) =>
            GetAuthorizationUrlFromWwwAuthenticate(
                GetServiceRootFromDataSourcePath(dataSourcePath)
            ),
        Resource = "https://myAadResourceValue.com", // Microsoft Entra ID resource value for your service - Guid or URL
        Scope = ".default"
    ]
]

其他身份验证类型

有关本文中未介绍的其他类型的身份验证的信息(如基于 Kerberos 的单一登录),请访问其他连接器功能文章了解详细信息。