快速入门:从 Android 应用将用户登录并调用 Microsoft Graph
在本快速入门中,你将下载并运行一个代码示例,该示例演示 Android 应用程序如何让用户登录并获取访问令牌来调用 Microsoft Graph API。
应用程序必须由 Microsoft Entra ID 中的应用对象表示,这样 Microsoft 标识平台便可以为应用程序提供令牌。
先决条件
- 具有活动订阅的 Azure 帐户。 创建帐户。
- Android Studio
- Android 16+
示例工作原理
代码已组织成多个片段,演示如何编写单帐户和多帐户 MSAL 应用。 代码文件的组织方式如下:
文件 | 演示 |
---|---|
MainActivity | 管理 UI |
MSGraphRequestWrapper | 使用 MSAL 提供的令牌调用 Microsoft Graph API |
MultipleAccountModeFragment | 初始化多帐户应用程序,加载用户帐户,并获取用于调用 Microsoft Graph API 的令牌 |
SingleAccountModeFragment | 初始化单帐户应用程序,加载用户帐户,并获取用于调用 Microsoft Graph API 的令牌 |
res/auth_config_multiple_account.json | 多帐户配置文件 |
res/auth_config_single_account.json | 单帐户配置文件 |
Gradle Scripts/build.grade (Module:app) | 此处添加了 MSAL 库依赖项 |
现在让我们更详细地探讨这些文件,并调用每个文件中 MSAL 特定的代码。
获取示例应用
注册示例应用
提示
本文中的步骤可能因开始使用的门户而略有不同。
至少以应用程序开发人员的身份登录到 Microsoft Entra 管理中心。
如果你有权访问多个租户,请使用顶部菜单中的“设置”图标 ,通过“目录 + 订阅”菜单切换到你希望在其中注册应用程序的租户。
浏览到“标识”>“应用程序”>“应用注册”。
选择“新注册”。
输入应用程序的名称。 应用的用户可能会看到此名称,你稍后可对其进行更改。
对于“支持的帐户类型”,请选择“任何组织目录(任何 Microsoft Entra 目录 - 多租户)中的帐户”。 要了解不同帐户类型的信息,请选择“帮我选择”选项。
选择“注册” 。
在“管理”下,选择“身份验证”>“添加平台”>“Android” 。
根据你在上面下载的示例类型输入项目的包名称。
- Java 示例 -
com.azuresamples.msalandroidapp
- Kotlin 示例 -
com.azuresamples.msalandroidkotlinapp
- Java 示例 -
在“配置 Android 应用”窗格的“签名哈希”部分,点击“生成开发签名哈希”,并将 KeyTool 命令复制到命令行。
- 安装 KeyTool.exe,使其作为 Java 开发工具包 (JDK) 的一部分。 还必须安装 OpenSSL 工具才能执行 KeyTool 命令。 有关详细信息,请参阅 有关生成密钥的 Android 文档。
生成由 KeyTool 生成的签名哈希。
选择“配置”并保存在“Android 配置”窗格中出现的“MSAL 配置”,以便稍后配置应用时输入该内容。
选择“完成” 。
配置示例应用
在 Android Studio 的项目窗格中,导航到 app\src\main\res。
右键单击“res” ,选择“新建” > “目录”。 输入
raw
作为新目录名称,然后选择“确定”。在 app>src>main>res>raw 中,转到名为
auth_config_single_account.json
的 JSON 文件,然后粘贴以前保存的 MSAL 配置。在“重定向 URI”下方,粘贴:
"account_mode" : "SINGLE",
配置文件应与如下示例类似:
{ "client_id": "00001111-aaaa-bbbb-3333-cccc4444", "authorization_user_agent": "WEBVIEW", "redirect_uri": "msauth://com.azuresamples.msalandroidapp/00001111%cccc4444%3D", "broker_redirect_uri_registered": true, "account_mode": "SINGLE", "authorities": [ { "type": "AAD", "audience": { "type": "AzureADMultipleOrgs", "tenant_id": "common" } } ] }
运行示例应用
从 Android Studio 的“可用设备”下拉列表中选择仿真器或物理设备,然后运行应用。
示例应用将在“单帐户模式”屏幕上启动。 默认情况下,会提供默认范围 user.read,在调用 Microsoft Graph API 期间读取你自己的配置文件数据时,将使用该范围。 默认提供 Microsoft Graph API 调用的 URL。 可根据需要更改这两个默认值。
使用应用菜单可在单帐户和多帐户模式之间切换。
在单帐户模式下,使用工作或家庭帐户登录:
- 选择“以交互方式获取图形数据”,以提示用户输入其凭据。 在屏幕底部可以看到调用 Microsoft Graph API 后的输出。
- 登录后,选择“以无提示方式获取图形数据”,以便在不再次提示用户输入凭据的情况下调用 Microsoft Graph API。 在屏幕底部可以看到调用 Microsoft Graph API 后的输出。
在多帐户模式下,可以重复相同的步骤。 此外,还可以删除登录的帐户,这也会删除该帐户的缓存令牌。
更多信息
阅读以下各部分来详细了解本快速入门。
将 MSAL 添加到应用
MSAL (com.microsoft.identity.client) 是一个库,用于用户登录和请求令牌,此类令牌用于访问受 Microsoft 标识平台保护的 API。 将以下内容添加到“Gradle 脚本”>“build.gradle (Module: app)”中的“Dependencies”下时,Gradle 3.0+ 将安装该库:
dependencies {
...
implementation 'com.microsoft.identity.client:msal:5.1.0'
implementation 'com.android.volley:volley:1.2.1'
...
}
此代码指示 Gradle 从 Maven Central 下载并生成 MSAL。
还必须在 dependencyResolutionManagement 下的“settings.gradle (Module: app)”的“allprojects”>“repositories”部分添加对 maven 的引用:
dependencyResolutionManagement {
...
maven
{
url 'https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1'
}
...
}
MSAL 导入
与 MSAL 库相关的导入为 com.microsoft.identity.client.*
。 例如,你将看到 import com.microsoft.identity.client.PublicClientApplication;
,它是表示公共客户端应用程序的 PublicClientApplication
类的命名空间。
SingleAccountModeFragment.java
此文件演示如何创建单帐户 MSAL 应用并调用 Microsoft Graph API。
单帐户应用仅供单个用户使用。 例如,你可能只使用一个帐户登录到映射应用。
单帐户 MSAL 初始化
在 SingleAccountModeFragment.java
的 onCreateView()
方法中,单个帐户 PublicClientApplication
是使用 auth_config_single_account.json
文件中存储的配置信息创建的。 通过以下方式初始化要在单帐户 MSAL 应用中使用的 MSAL 库:
...
// Creates a PublicClientApplication object with res/raw/auth_config_single_account.json
PublicClientApplication.createSingleAccountPublicClientApplication(getContext(),
R.raw.auth_config_single_account,
new IPublicClientApplication.ISingleAccountApplicationCreatedListener() {
@Override
public void onCreated(ISingleAccountPublicClientApplication application) {
/**
* This test app assumes that the app is only going to support one account.
* This requires "account_mode" : "SINGLE" in the config json file.
**/
mSingleAccountApp = application;
loadAccount();
}
@Override
public void onError(MsalException exception) {
displayError(exception);
}
});
将用户登录
在 SingleAccountModeFragment.java
中,将用户登录的代码位于 signInButton
单击处理程序中的 initializeUI()
内。
在尝试获取令牌之前调用 signIn()
。 signIn()
的行为如同调用 acquireToken()
,将以交互方式提示用户登录。
将用户登录是一个异步操作。 将传递一个回调用于调用 Microsoft Graph API,并在用户登录后更新 UI:
mSingleAccountApp.signIn(getActivity(), null, getScopes(), getAuthInteractiveCallback());
将用户注销
在 SingleAccountModeFragment.java
中,将用户注销的代码位于 signOutButton
单击处理程序中的 initializeUI()
内。 将用户注销是一个异步操作。 将用户注销还会清除该帐户的令牌缓存。 将用户帐户注销后,会创建一个回调来更新 UI:
mSingleAccountApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() {
@Override
public void onSignOut() {
updateUI(null);
performOperationOnSignOut();
}
@Override
public void onError(@NonNull MsalException exception) {
displayError(exception);
}
});
以交互方式或无提示方式获取令牌
为了尽量减少提示用户的次数,你通常会以无提示方式获取令牌。 如果出错,可尝试以交互方式获取令牌。 应用首次调用 signIn()
时,它将有效充当 acquireToken()
的调用,这会提示用户提供凭据。
在某些情况下,系统可能会提示用户选择其帐户、输入其凭据,或者许可应用请求的权限:
- 用户首次登录到应用程序
- 用户在重置其密码时需输入其凭据。
- 如果许可已撤销
- 如果应用显式要求许可
- 应用程序首次请求访问资源时
- 需要 MFA 或其他条件访问策略时
通过涉及用户的 UI 以交互方式获取令牌的代码位于 initializeUI()
(在 callGraphApiInteractiveButton
中单击处理程序)中的 SingleAccountModeFragment.java
内:
/**
* If acquireTokenSilent() returns an error that requires an interaction (MsalUiRequiredException),
* invoke acquireToken() to have the user resolve the interrupt interactively.
*
* Some example scenarios are
* - password change
* - the resource you're acquiring a token for has a stricter set of requirement than your Single Sign-On refresh token.
* - you're introducing a new scope which the user has never consented for.
**/
mSingleAccountApp.acquireToken(getActivity(), getScopes(), getAuthInteractiveCallback());
如果用户已登录,则 acquireTokenSilentAsync()
允许应用以无提示方式请求 callGraphApiSilentButton
单击处理程序中的 initializeUI()
内静默显示的令牌:
/**
* Once you've signed the user in,
* you can perform acquireTokenSilent to obtain resources without interrupting the user.
**/
mSingleAccountApp.acquireTokenSilentAsync(getScopes(), AUTHORITY, getAuthSilentCallback());
加载帐户
用于加载帐户的代码位于 SingleAccountModeFragment.java
中的 loadAccount()
内。 加载用户帐户是一个异步操作,因此,在帐户加载、更改或出错时要处理的回调将传递到 MSAL。 以下代码也会处理在删除了帐户、用户切换到其他帐户等情况时发生的 onAccountChanged()
。
private void loadAccount() {
...
mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() {
@Override
public void onAccountLoaded(@Nullable IAccount activeAccount) {
// You can use the account data to update your UI or your app database.
updateUI(activeAccount);
}
@Override
public void onAccountChanged(@Nullable IAccount priorAccount, @Nullable IAccount currentAccount) {
if (currentAccount == null) {
// Perform a cleanup task as the signed-in account changed.
performOperationOnSignOut();
}
}
@Override
public void onError(@NonNull MsalException exception) {
displayError(exception);
}
});
调用 Microsoft Graph
用户登录后,将通过 SingleAccountModeFragment.java
中定义的 callGraphAPI()
的 HTTP 请求调用 Microsoft Graph。 此函数是一个简化示例的包装器,它会执行从 authenticationResult
获取访问令牌等某些任务,打包 MSGraphRequestWrapper 的调用,并显示调用结果。
private void callGraphAPI(final IAuthenticationResult authenticationResult) {
MSGraphRequestWrapper.callGraphAPIUsingVolley(
getContext(),
graphResourceTextView.getText().toString(),
authenticationResult.getAccessToken(),
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
/* Successfully called graph, process data and send to UI */
...
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
...
}
});
}
auth_config_single_account.json
这是使用单个帐户的 MSAL 应用的配置文件。
有关这些字段的说明,请参阅了解 Android MSAL 配置文件。
请注意 "account_mode" : "SINGLE"
,它会将此应用配置为使用单个帐户。
"client_id"
已预配置为使用 Microsoft 维护的应用对象注册。
"redirect_uri"
已预配置为使用代码示例随附的签名密钥。
{
"client_id": "00001111-aaaa-2222-bbbb-3333cccc4444",
"authorization_user_agent": "WEBVIEW",
"redirect_uri": "msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D",
"account_mode": "SINGLE",
"broker_redirect_uri_registered": true,
"authorities": [
{
"type": "AAD",
"audience": {
"type": "AzureADMultipleOrgs",
"tenant_id": "common"
}
}
]
}
MultipleAccountModeFragment.java
此文件演示如何创建多帐户 MSAL 应用并调用 Microsoft Graph API。
邮件应用就是多帐户应用的一个例子,它允许使用多个用户帐户,例如工作帐户。
多帐户 MSAL 初始化
在 MultipleAccountModeFragment.java
文件中的 onCreateView()
内,多帐户应用对象 (IMultipleAccountPublicClientApplication
) 是使用 auth_config_multiple_account.json file
中存储的配置信息创建的:
// Creates a PublicClientApplication object with res/raw/auth_config_multiple_account.json
PublicClientApplication.createMultipleAccountPublicClientApplication(getContext(),
R.raw.auth_config_multiple_account,
new IPublicClientApplication.IMultipleAccountApplicationCreatedListener() {
@Override
public void onCreated(IMultipleAccountPublicClientApplication application) {
mMultipleAccountApp = application;
loadAccounts();
}
@Override
public void onError(MsalException exception) {
...
}
});
创建的 MultipleAccountPublicClientApplication
对象存储在某个类成员变量中,因此可以使用该对象来与 MSAL 库交互,以获取令牌以及加载和删除用户帐户。
加载帐户
多帐户应用通常调用 getAccounts()
来选择要用于 MSAL 操作的帐户。 用于加载帐户的代码位于 loadAccounts()
中的 MultipleAccountModeFragment.java
文件内。 加载用户帐户是一个异步操作。 因此,某个回调会处理帐户已加载、更改或出错时的情况。
/**
* Load currently signed-in accounts, if there's any.
**/
private void loadAccounts() {
if (mMultipleAccountApp == null) {
return;
}
mMultipleAccountApp.getAccounts(new IPublicClientApplication.LoadAccountsCallback() {
@Override
public void onTaskCompleted(final List<IAccount> result) {
// You can use the account data to update your UI or your app database.
accountList = result;
updateUI(accountList);
}
@Override
public void onError(MsalException exception) {
displayError(exception);
}
});
}
以交互方式或无提示方式获取令牌
在某些情况下,系统可能会提示用户选择其帐户、输入其凭据,或者许可应用请求的权限:
- 用户首次登录应用程序
- 用户在重置其密码时需输入其凭据。
- 如果许可已撤销
- 如果应用显式要求许可
- 应用程序首次请求访问资源时
- 需要 MFA 或其他条件访问策略时
多帐户应用通常使用 acquireToken()
调用通过涉及用户的 UI 以交互方式获取令牌。 以交互方式获取令牌的代码位于 callGraphApiInteractiveButton
单击处理程序中的 MultipleAccountModeFragment.java
文件的 initializeUI()
内:
/**
* Acquire token interactively. It will also create an account object for the silent call as a result (to be obtained by getAccount()).
*
* If acquireTokenSilent() returns an error that requires an interaction,
* invoke acquireToken() to have the user resolve the interrupt interactively.
*
* Some example scenarios are
* - password change
* - the resource you're acquiring a token for has a stricter set of requirement than your SSO refresh token.
* - you're introducing a new scope which the user has never consented for.
**/
mMultipleAccountApp.acquireToken(getActivity(), getScopes(), getAuthInteractiveCallback());
应用不应该在用户每次请求令牌时都要求他们登录。 如果用户已登录,则 acquireTokenSilentAsync()
允许应用以无提示方式请求令牌,如 callGraphApiSilentButton
单击处理程序的 MultipleAccountModeFragment.java
文件的 initializeUI()
中所示:
/**
* Performs acquireToken without interrupting the user.
*
* This requires an account object of the account you're obtaining a token for.
* (can be obtained via getAccount()).
*/
mMultipleAccountApp.acquireTokenSilentAsync(getScopes(),
accountList.get(accountListSpinner.getSelectedItemPosition()),
AUTHORITY,
getAuthSilentCallback());
删除帐户
用于删除帐户以及该帐户的所有已缓存令牌的代码位于“删除帐户”按钮的处理程序的 MultipleAccountModeFragment.java
文件中的 initializeUI()
内。 在删除帐户之前,需要提供从 getAccounts()
和 acquireToken()
等 MSAL 方法获取的帐户对象。 由于删除帐户是一个异步操作,因此需提供 onRemoved
回调来更新 UI。
/**
* Removes the selected account and cached tokens from this app (or device, if the device is in shared mode).
**/
mMultipleAccountApp.removeAccount(accountList.get(accountListSpinner.getSelectedItemPosition()),
new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
@Override
public void onRemoved() {
...
/* Reload account asynchronously to get the up-to-date list. */
loadAccounts();
}
@Override
public void onError(@NonNull MsalException exception) {
displayError(exception);
}
});
auth_config_multiple_account.json
这是使用多个帐户的 MSAL 应用的配置文件。
有关各种字段的说明,请参阅了解 Android MSAL 配置文件。
与 auth_config_single_account.json 配置文件不同,此配置文件包含 "account_mode" : "MULTIPLE"
而不是 "account_mode" : "SINGLE"
,因为这是一个多帐户应用。
"client_id"
已预配置为使用 Microsoft 维护的应用对象注册。
"redirect_uri"
已预配置为使用代码示例随附的签名密钥。
{
"client_id": "00001111-aaaa-2222-bbbb-3333cccc4444",
"authorization_user_agent": "WEBVIEW",
"redirect_uri": "msauth://com.azuresamples.msalandroidapp/1wIqXSqBj7w%2Bh11ZifsnqwgyKrY%3D",
"account_mode": "MULTIPLE",
"broker_redirect_uri_registered": true,
"authorities": [
{
"type": "AAD",
"audience": {
"type": "AzureADMultipleOrgs",
"tenant_id": "common"
}
}
]
}
帮助和支持
如果需要帮助、需要报告问题,或者需要详细了解支持选项,请参阅面向开发人员的帮助和支持。
后续步骤
继续学习 Android 教程,在该教程中,你将生成一个从 Microsoft 标识平台获取访问令牌并使用它来调用 Microsoft Graph API 的 Android 应用。