前言
微软的生态建设很强大,不仅拥有许多非常好用的产品与工具,而且各产品通常也会提供对应的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应用注册与访问令牌的获取方式,但可能忽略了部分细节,因此这里给出一些相关的官方文档链接以供参考。
[2] 权限和同意简介
[3] Microsoft 标识平台和 OAuth 2.0 客户端凭据流