回忆是一座桥
却是通往寂寞的牢

Azure应用注册与访问令牌获取

前言

微软的生态建设很强大,不仅拥有许多非常好用的产品与工具,而且各产品通常也会提供对应的REST API服务,让开发者能够在自己的应用程序中利用开放的API接口来调用微软的服务与各产品的功能。但是想要调用微软各产品的API,则需要先获取到访问令牌,并在调用API时将访问令牌一起发送。访问令牌类似于账号密码,代表了以某个身份和其拥有的权限去访问微软的产品与服务。而获取访问令牌前则需要注册Azure应用,Azure应用代表了所开发的应用程序,通过Azure应用可以方便的管理应用程序所具有的API权限。

关于Azure应用与访问令牌的相关概念和细节这里就不过多介绍,若感兴趣可以去浏览微软官方文档。

注册Azure应用

1、登录Azure门户,并打开Azure AD

2、在AzureAD的左侧菜单中点击应用注册选项

3、然后点击新注册按钮

4、输入Azure应用的名称,并点击注册

5、配置身份验证,将“允许公共客户端流”选项设置为是,然后保存

6、创建客户端密码

7、保存刚创建的客户端密码,即下图所示的值,该密码只显示一次,注意保存,后面要用到

8、添加所需要的API权限,在弹出的窗口中选择对应的服务,比如PowerBI Service

9、记录所选服务对应的Url,即下图红框处。该Url添加.default后缀后即为完整的Scope Url,该Scope Url在后续申请所选服务的API访问令牌时要用到,需要提前保存。如下图所示的PowerBI Service给出的Url为:https://analysis.windows.net/powerbi/api/ ,那么其完整的Scope Url即为:https://analysis.windows.net/powerbi/api/.default

10、选择权限类别,若使用账号密码作为凭证则选择委托的权限,若使用Azure应用的客户端密码作为凭证则选择应用程序权限。如果区分不了,那也可以把两种类别的所需权限都添加进来

11、添加完权限后,将在已配置权限列表中显示。这里的权限也称为范围,代表Azure应用理论上能够拥有的最大权限,而Azure应用实际拥有的权限还需要与使用者所具有的权限取交集

12、给添加的权限授予管理员同意

13、回到Azure应用的概述页面,记录客户端ID与租户ID,后面要用到

14、至此,Azure应用已注册并配置完成,而且还收集到后续需要使用的信息,如客户端ID、租户ID、客户端密码、SCOPE URL等

获取访问令牌

获取访问令牌时需要向微软标识平台发送相关信息,最常用的方式是OAuth2.0令牌授予流。简单来说就是一个API接口,向该API接口发送相关信息后,若通过微软标识平台的验证,则会返回访问令牌。

OAuth2.0令牌授予流有多种类型,这里只介绍两种最常用的,也是最方便操作的,其余类型可以自行浏览微软标识平台的官方文档。


资源所有者密码凭据授予

该方式需要使用账号密码来获取访问令牌,因此又称为主用户模式,没有密码或启用了多重身份验证(MFA)的账号则不能使用该方式。若使用该令牌授予流,需要给Azure应用添加委托的权限。

API接口:

Url:https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token

Method:POST
Content-Type: application/x-www-form-urlencoded

Request Body:
client_id=<client_id>
&scope=<scope_url>
&username=<user_account>
&password=<user_password>
&grant_type=password

参数说明:

参数 说明
tenant 可以使用Azure租户的ID或名称,也可以直接设置为 organizations
client_id 所注册的Azure应用的应用程序(客户端)ID
grant_type 必须设置为 password
user_account 用户的电子邮件地址
user_password 用户的密码
scope_url 声明需使用的资源或服务,获取方式请参考上文

客户端密码凭据授予

使用应用的客户端密码来获取访问令牌,通常用于在后台运行而无需用户参与的守护程序,因此又称为服务主体模式。若使用该令牌授予流,需要给Azure应用添加应用程序权限。

API接口:

Url:https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token

Method:POST
Content-Type: application/x-www-form-urlencoded

Request Body:
client_id=<client_id>
&scope=<scope_url>
&client_secret=<client_secret>
&grant_type=client_credentials

参数说明:

参数 说明
tenant 只可以使用Azure租户的ID或名称
client_id 所注册的Azure应用的应用程序(客户端)ID
client_secret 所注册的Azure应用的客户端密码
grant_type 必须设置为 client_credentials
scope_url 声明需使用的资源或服务,获取方式请参考上文

现成的脚本代码

上面介绍了Azure应用注册与获取访问令牌的方法或原理,主要涉及到API的调用与请求,若不熟悉API的相关知识,则阅读起来可能会稍微有一点技术门槛。因此下面直接给出PowerQuery与Python的两种现成实现代码,只需要注册Azure应用并填写相关参数即可,支持国际版与21世纪互联两个环境,并可选主用户模式或服务主体模式,可以自行选择其一。


PowerQuery OAuth2.0 :

let
    // Define a function to acquire access token. 
    get_access_token = (ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE,AUTHORITY_URL) =>
        /*
        Author: 夕枫
        Function Description: 
            Generates and returns Access token
        Returns:
            string: Access token

        Below params if don't need to use, pls use blank string to replace, such as: "" 

        ENVIRONMENT_TYPE :      Can be set to 'Global' or '21Vianet'
        AUTHENTICATION_MODE :   Can be set to 'MasterUser' or 'ServicePrincipal'
        CLIENT_ID :             Client Id (Application Id) of the AAD app
        TENANT_ID :             Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
        CLIENT_SECRET :         Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
        USER_ACCOUNT :          Master user email address. Required only for MasterUser authentication mode.
        USER_PASSWORD :         Master user email password. Required only for MasterUser authentication mode.
        SCOPE_BASE :            Scope Base of AAD app. Required when the REST API dosn't is PowerBI REST API.
        AUTHORITY_URL :         URL used for initiating authorization request, can set to blank string, will use default URL. Required when the default URL fail.
        */
        try
            if not List.Contains({"global","21vianet"},Text.Lower(ENVIRONMENT_TYPE)) then
                error "Error! Pls check the input of ENVIRONMENT_TYPE."
            else let
                SCOPE_BASE = if SCOPE_BASE<>"" then SCOPE_BASE else if Text.Lower(ENVIRONMENT_TYPE)="global" then "https://analysis.windows.net/powerbi/api/.default" else "https://analysis.chinacloudapi.cn/powerbi/api/.default",
                AUTHORITY_URL = if AUTHORITY_URL<>"" then SCOPE_BASE else if Text.Lower(ENVIRONMENT_TYPE)="global" then "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" else "https://login.chinacloudapi.cn/organizations/oauth2/v2.0/token",
                RESPONSE = 
                    if Text.Lower(AUTHENTICATION_MODE) = "masteruser" then
                        Web.Contents(
                            AUTHORITY_URL,
                            [
                                Headers = [#"Content-Type"="application/x-www-form-urlencoded"],
                                Content = 
                                    Text.ToBinary(
                                        Text.Format(
                                            "client_id=#{0}&scope=#{1}&username=#{2}&password=#{3}&grant_type=password",
                                            {CLIENT_ID,SCOPE_BASE,USER_ACCOUNT,USER_PASSWORD}
                                        )
                                    )
                            ]
                        )
                    else if Text.Lower(AUTHENTICATION_MODE) = "serviceprincipal" then
                        Web.Contents(
                            Text.Replace(AUTHORITY_URL,"organizations",TENANT_ID),
                            [
                                Headers = [#"Content-Type"="application/x-www-form-urlencoded"],
                                Content = 
                                    Text.ToBinary(
                                        Text.Format(
                                            "client_id=#{0}&scope=#{1}&client_secret=#{2}&grant_type=client_credentials",
                                            {CLIENT_ID,SCOPE_BASE,CLIENT_SECRET}
                                        )
                                    )
                            ]
                        )
                    else
                        error "Error! Pls check the input of AUTHENTICATION_MODE."
                in "Bearer " & Json.Document(RESPONSE)[access_token]
        otherwise error "Error retrieving Access token, pls check the input params.",

    // ------------------------------------------------config setting-----------------------------------------------------
    // Can be set to 'Global' or '21Vianet'
    ENVIRONMENT_TYPE = "Global",

    // Can be set to 'MasterUser' or 'ServicePrincipal'
    AUTHENTICATION_MODE = "ServicePrincipal",

    // Client Id (Application Id) of the AAD app
    CLIENT_ID = "Input CLIENT_ID here.",

    // Below params if don't need to use, pls use blank string to replace, such as: '' 

    // Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
    TENANT_ID = "Input TENANT_ID here.",

    // Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
    CLIENT_SECRET = "Input CLIENT_SECRET here.",

    // Master user email address. Required only for MasterUser authentication mode.
    USER_ACCOUNT = "",

    // Master user email password. Required only for MasterUser authentication mode.
    USER_PASSWORD = "",

    // If Use PowerBI REST API, the below parma can set to blank string. Otherwise, pls input the available url of other REST API.
    SCOPE_BASE = "",

    // The below parma can set to blank string to use default AUTHORITY_URL. If the default setting fail, pls input the available url.
    AUTHORITY_URL = "",

    // ------------------------------------------------config setting end-------------------------------------------------

    TOKEN = get_access_token(ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE,AUTHORITY_URL)

    // -------------------------------------------Call REST API by above TOKEN--------------------------------------------

    // CallAPI = xxxxx

in
    TOKEN

Python OAuth2.0 :

import requests
import os

def get_access_token(ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE='',AUTHORITY_URL=''):
    '''
    Author: 夕枫
    Function Description: 
        Generates and returns Access token
    Returns:
        string: Access token

    Below params if don't need to use, pls use blank string to replace, such as: '' 

    ENVIRONMENT_TYPE :      Can be set to 'Global' or '21Vianet'
    AUTHENTICATION_MODE :   Can be set to 'MasterUser' or 'ServicePrincipal'
    CLIENT_ID :             Client Id (Application Id) of the AAD app
    TENANT_ID :             Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
    CLIENT_SECRET :         Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
    USER_ACCOUNT :          Master user email address. Required only for MasterUser authentication mode.
    USER_PASSWORD :         Master user email password. Required only for MasterUser authentication mode.
    SCOPE_BASE :            Scope Base of AAD app. Required when the REST API dosn't is PowerBI REST API.
    AUTHORITY_URL :         URL used for initiating authorization request, can set to blank string, will use default URL. Required when the default URL fail.
    '''
    if ENVIRONMENT_TYPE.lower() not in ['global','21vianet']:
        return 'Error! Pls check the input of ENVIRONMENT_TYPE.'

    if SCOPE_BASE=='':
        SCOPE_BASE = 'https://analysis.windows.net/powerbi/api/.default' if ENVIRONMENT_TYPE.lower()=="global" else 'https://analysis.chinacloudapi.cn/powerbi/api/.default'
    if AUTHORITY_URL=='':
        AUTHORITY_URL = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token' if ENVIRONMENT_TYPE.lower()=="global" else 'https://login.chinacloudapi.cn/organizations/oauth2/v2.0/token'

    try:
        if AUTHENTICATION_MODE.lower() == 'masteruser':
            response = requests.post(
                AUTHORITY_URL,
                headers={'Content-Type':'application/x-www-form-urlencoded'},
                data={
                    'client_id':CLIENT_ID,
                    'scope':SCOPE_BASE,
                    'username':USER_ACCOUNT,
                    'password':USER_PASSWORD,
                    'grant_type':'password'
                }
            )
        elif AUTHENTICATION_MODE.lower() == 'serviceprincipal':
            response = requests.post(
                AUTHORITY_URL.replace('organizations', TENANT_ID),
                headers={'Content-Type':'application/x-www-form-urlencoded'},
                data={
                    'client_id':CLIENT_ID,
                    'scope':SCOPE_BASE,
                    'client_secret':CLIENT_SECRET,
                    'grant_type':'client_credentials'
                }
            )
        else:
            return 'Error! Pls check the input of AUTHENTICATION_MODE.'

        return 'Bearer ' + response.json()['access_token']

    except Exception as ex:
        return 'Error retrieving Access token : '+str(ex)

if __name__ == '__main__':
    # ----------------------------------------------------------config setting----------------------------------------------------------------

    # Can be set to 'Global' or '21Vianet'
    ENVIRONMENT_TYPE = 'Global'      

    # Can be set to 'MasterUser' or 'ServicePrincipal'
    AUTHENTICATION_MODE = 'ServicePrincipal'

    # Client Id (Application Id) of the AAD app
    CLIENT_ID = 'Input CLIENT_ID here.'

    # Below params if don't need to use, pls use blank string to replace, such as: '' 

    # Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
    TENANT_ID = 'Input TENANT_ID here.'

    # Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
    CLIENT_SECRET = 'Input CLIENT_SECRET here.'

    # Master user email address. Required only for MasterUser authentication mode.
    USER_ACCOUNT = ''

    # Master user email password. Required only for MasterUser authentication mode.
    USER_PASSWORD = ''

    # If Use PowerBI REST API, the below parma can set to blank string. Otherwise, pls input the available url of other REST API.
    SCOPE_BASE = ''

    # The below parma can set to blank string to use default AUTHORITY_URL. If the default setting fail, pls input the available url.
    AUTHORITY_URL = ''

    # ---------------------------------------------------------config setting end---------------------------------------------------------------

    token = get_access_token(ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE,AUTHORITY_URL)
    print(token)

    os.system('pause')

Python MSAL :

import msal
import os

def get_access_token(ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE='',AUTHORITY_URL=''):
    '''
    Author: 夕枫
    Function Description: 
        Generates and returns Access token
    Returns:
        string: Access token

    Below params if don't need to use, pls use blank string to replace, such as: '' 

    ENVIRONMENT_TYPE :      Can be set to 'Global' or '21Vianet'
    AUTHENTICATION_MODE :   Can be set to 'MasterUser' or 'ServicePrincipal'
    CLIENT_ID :             Client Id (Application Id) of the AAD app
    TENANT_ID :             Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
    CLIENT_SECRET :         Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
    USER_ACCOUNT :          Master user email address. Required only for MasterUser authentication mode.
    USER_PASSWORD :         Master user email password. Required only for MasterUser authentication mode.
    SCOPE_BASE :            Scope Base of AAD app. Required when the REST API dosn't is PowerBI REST API.
    AUTHORITY_URL :         URL used for initiating authorization request, can set to blank string, will use default URL. Required when the default URL fail.
    '''
    if ENVIRONMENT_TYPE.lower() not in ['global','21vianet']:
        return 'Error! Pls check the input of ENVIRONMENT_TYPE.'

    if SCOPE_BASE=='':
        SCOPE_BASE = ['https://analysis.windows.net/powerbi/api/.default'] if ENVIRONMENT_TYPE.lower()=="global" else ['https://analysis.chinacloudapi.cn/powerbi/api/.default']
    if AUTHORITY_URL=='':
        AUTHORITY_URL = 'https://login.microsoftonline.com/organizations' if ENVIRONMENT_TYPE.lower()=="global" else 'https://login.chinacloudapi.cn/organizations'

    try:
        response = None
        if AUTHENTICATION_MODE.lower() == 'masteruser':
            # Create a public client to authorize the app with the AAD app
            clientapp = msal.PublicClientApplication(CLIENT_ID, authority=AUTHORITY_URL)
            accounts = clientapp.get_accounts(username=USER_ACCOUNT)

            if accounts:
                # Retrieve Access token from user cache if available
                response = clientapp.acquire_token_silent(SCOPE_BASE, account=accounts[0])

            if not response:
                # Make a client call if Access token is not available in cache
                response = clientapp.acquire_token_by_username_password(USER_ACCOUNT, USER_PASSWORD, scopes=SCOPE_BASE) 

        elif AUTHENTICATION_MODE.lower() == 'serviceprincipal':
            authority = AUTHORITY_URL.replace('organizations', TENANT_ID)
            clientapp = msal.ConfidentialClientApplication(CLIENT_ID, client_credential=CLIENT_SECRET, authority=authority)

            # Make a client call if Access token is not available in cache
            response = clientapp.acquire_token_for_client(scopes=SCOPE_BASE)

        else:
            return 'Error! Pls check the input of AUTHENTICATION_MODE.'

        return 'Bearer ' + response['access_token']

    except Exception as ex:
        return 'Error retrieving Access token : '+str(ex)

if __name__ == '__main__':
    # ----------------------------------------------------------config setting----------------------------------------------------------------

    # Can be set to 'Global' or '21Vianet'
    ENVIRONMENT_TYPE = 'Global'      

    # Can be set to 'MasterUser' or 'ServicePrincipal'
    AUTHENTICATION_MODE = 'ServicePrincipal'

    # Client Id (Application Id) of the AAD app
    CLIENT_ID = 'Input CLIENT_ID here.'

    # Below params if don't need to use, pls use blank string to replace, such as: '' 

    # Id of the Azure tenant in which AAD app and Power BI report is hosted. Required only for ServicePrincipal authentication mode.
    TENANT_ID = 'Input TENANT_ID here.'

    # Client Secret (App Secret) of the AAD app. Required only for ServicePrincipal authentication mode.
    CLIENT_SECRET = 'Input CLIENT_SECRET here.'

    # Master user email address. Required only for MasterUser authentication mode.
    USER_ACCOUNT = ''

    # Master user email password. Required only for MasterUser authentication mode.
    USER_PASSWORD = ''

    # If Use PowerBI REST API, the below parma can set to blank string. Otherwise, pls input the available url of other REST API. And the input scopes should be a list, tuple, or set.
    SCOPE_BASE = ''

    # The below parma can set to blank string to use default AUTHORITY_URL. If the default setting fail, pls input the available url.
    AUTHORITY_URL = ''

    # ---------------------------------------------------------config setting end---------------------------------------------------------------

    token = get_access_token(ENVIRONMENT_TYPE,AUTHENTICATION_MODE,TENANT_ID,CLIENT_ID,CLIENT_SECRET,USER_ACCOUNT,USER_PASSWORD,SCOPE_BASE,AUTHORITY_URL)
    print(token)

    os.system('pause')  

总结

本篇文章介绍了Azure应用注册与访问令牌的获取方式,但可能忽略了部分细节,因此这里给出一些相关的官方文档链接以供参考。

[1] 什么是 Microsoft 标识平台?

[2] 权限和同意简介

[3] Microsoft 标识平台和 OAuth 2.0 客户端凭据流

[4] Microsoft 标识平台和 OAuth 2.0 资源所有者密码凭据

[5] Microsoft 身份验证库 (MSAL) 的概述

未经允许不得转载:夕枫 » Azure应用注册与访问令牌获取
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论