适用于 Android 的 ADAL 到 MSAL 迁移指南ADAL to MSAL migration guide for Android

本文重点介绍需要做出哪些更改,才能迁移使用 Azure Active Directory 身份验证库 (ADAL) 的应用,使之使用 Microsoft 身份验证库 (MSAL)。This article highlights changes you need to make to migrate an app that uses the Azure Active Directory Authentication Library (ADAL) to use the Microsoft Authentication Library (MSAL).

差异重点说明Difference highlights

ADAL 适用于 Azure Active Directory v1.0 终结点。ADAL works with the Azure Active Directory v1.0 endpoint. Microsoft 身份验证库 (MSAL) 适用于 Microsoft 标识平台--前称为 Azure Active Directory v2.0 终结点。The Microsoft Authentication Library (MSAL) works with the Microsoft identity platform--formerly known as the Azure Active Directory v2.0 endpoint. Microsoft 标识平台与 Azure Active Directory v1.0 的不同之处在于:The Microsoft identity platform differs from Azure Active Directory v1.0 in that it:

支持:Supports:

  • 组织标识 (Azure Active Directory)Organizational Identity (Azure Active Directory)

  • 与以下协议的标准兼容:Is standards compatible with:

    • OAuth v2.0OAuth v2.0
    • OpenID Connect (OIDC)OpenID Connect (OIDC)

MSAL 公共 API 引入了重要的更改,其中包括:The MSAL public API introduces important changes, including:

  • 用于访问令牌的新模型:A new model for accessing tokens:
    • ADAL 允许通过 AuthenticationContext(表示服务器)访问令牌。ADAL provides access to tokens via the AuthenticationContext, which represents the server. MSAL 允许通过 PublicClientApplication(表示客户端)访问令牌。MSAL provides access to tokens via the PublicClientApplication, which represents the client. 客户端开发人员无需为每个要交互的颁发机构创建新的 PublicClientApplication 实例。Client developers don't need to create a new PublicClientApplication instance for every Authority they need to interact with. 只需一个 PublicClientApplication 配置。Only one PublicClientApplication configuration is required.
    • 除使用资源标识符以外,还支持使用范围请求访问令牌。Support for requesting access tokens using scopes in addition to resource identifiers.
    • 对增量许可的支持。Support for incremental consent. 当用户在应用中访问越来越多的功能时,开发人员可以请求范围,包括应用注册期间未包含的范围。Developers can request scopes as the user accesses more and more functionality in the app, including those not included during app registration.
    • 不再在运行时验证颁发机构,Authorities are no longer validated at run-time. 而是由开发人员在开发过程中声明“已知颁发机构”列表。Instead, the developer declares a list of 'known authorities' during development.
  • 令牌 API 更改:Token API changes:
    • 在 ADAL 中,AcquireToken() 首先发出静默请求。In ADAL, AcquireToken() first makes a silent request. 如果该请求失败,则发出交互式请求。Failing that, it makes an interactive request. 此行为导致某些开发人员仅依赖于 AcquireToken,这又导致系统有时候以意外方式提示用户提供凭据。This behavior resulted in some developers relying only on AcquireToken, which resulted in the user being unexpectedly prompted for credentials at times. MSAL 要求开发人员留意用户收到 UI 提示的时间。MSAL requires developers be intentional about when the user receives a UI prompt.
      • AcquireTokenSilent 始终生成成功或失败的静默请求。AcquireTokenSilent always results in a silent request that either succeeds or fails.
      • AcquireToken 始终生成一个通过 UI 提示用户的请求。AcquireToken always results in a request that prompts the user via UI.
  • MSAL 支持来自默认浏览器或嵌入式 Web 视图的登录:MSAL supports sign in from either a default browser or an embedded web view:
    • 默认情况下,使用设备上的默认浏览器。By default, the default browser on the device is used. 这样,MSAL 就可以使用已为一个或多个登录帐户提供的身份验证状态 (Cookie)。This allows MSAL to use authentication state (cookies) that may already be present for one or more signed in accounts. 如果未提供身份验证状态,则在授权期间通过 MSAL 进行的身份验证将导致创建身份验证状态 (Cookie),以便为同一浏览器中使用的其他 Web 应用程序提供便利。If no authentication state is present, authenticating during authorization via MSAL results in authentication state (cookies) being created for the benefit of other web applications that will be used in the same browser.
  • 新的异常模型:New exception Model:
    • 异常更明确地定义了发生的错误类型,以及开发人员需要采取哪些措施来解决该错误。Exceptions more clearly define the type of error that occurred and what the developer needs to do to resolve it.
  • MSAL 支持 AcquireTokenAcquireTokenSilent 调用的参数对象。MSAL supports parameter objects for AcquireToken and AcquireTokenSilent calls.
  • MSAL 支持以下项的声明性配置:MSAL supports declarative configuration for:
    • 客户端 ID、重定向 URI。Client ID, Redirect URI.
    • 嵌入式浏览器与默认浏览器Embedded vs Default Browser
    • 颁发机构Authorities
    • 读取和连接超时等 HTTP 设置HTTP settings such as read and connection timeout

应用注册以及到 MSAL 的迁移Your app registration and migration to MSAL

不需更改现有应用注册即可使用 MSAL。You don't need to change your existing app registration to use MSAL. 若要利用增量/渐进式许可,可能需要查看注册,以识别要增量请求的特定范围。If you want to take advantage of incremental/progressive consent, you may need to review the registration to identify the specific scopes that you want to request incrementally. 下面是有关范围和增量许可的详细信息。More information on scopes and incremental consent follows.

在门户上的应用注册中,将会看到“API 权限”选项卡。 其中提供了应用当前配置为请求访问的 API 和权限(范围)的列表。In your app registration in the portal, you will see an API permissions tab. This provides a list of the APIs and permissions (scopes) that your app is currently configured to request access to. 其中还显示与每个 API 权限关联的范围名称列表。It also shows a list of the scope names associated with each API permission.

首次使用 ADAL 和 AAD v1 终结点时,就会授予用户对其拥有的资源的许可。With ADAL and the AAD v1 endpoint, user consent to resources they own was granted on first use. 通过 MSAL 和 Microsoft 标识平台时,可以增量请求许可。With MSAL and the Microsoft identity platform, consent can be requested incrementally. 对于被用户视为高特权的权限,或者对为何需要某个权限提供明确的解释时,增量许可非常有用。Incremental consent is useful for permissions that a user may consider high privilege, or may otherwise question if not provided with a clear explanation of why the permission is required. 在 ADAL 中,这些权限可能导致用户放弃应用登录。In ADAL, those permissions may have resulted in the user abandoning signing in to your app.

提示

如果需要向用户提供额外的上下文来解释为何应用需要某个权限,我们建议使用增量许可。We recommend the use of incremental consent in scenarios where you need to provide additional context to your user about why your app needs a permission.

组织管理员可以代表其组织中的所有成员来许可应用程序所需的权限。Organization administrators can consent to permissions your application requires on behalf of all of the members of their organization. 某些组织仅允许管理员向应用程序提供许可。Some organizations only allow admins to consent to applications. 管理员许可要求在应用注册中包含应用程序使用的所有 API 权限和范围。Admin consent requires that you include all API permissions and scopes used by your application in your app registration.

提示

即使可以使用 MSAL 来为应用注册中未包含的活动请求范围,我们也建议更新应用注册,以包含用户可以授权访问的所有资源和范围。Even though you can request a scope using MSAL for something not included in your app registration, we recommend that you update your app registration to include all resources and scopes that a user could ever grant permission to.

从资源 ID 迁移到范围Migrating from resource IDs to scopes

首次使用时进行身份验证并请求所有权限的授权Authenticate and request authorization for all permissions on first use

如果目前使用的是 ADAL 且不需要使用增量许可,则开始使用 MSAL 的最简单方法是使用新的 AcquireTokenParameter 对象并设置资源 ID 值来发出 acquireToken 请求。If you're currently using ADAL and don't need to use incremental consent, the simplest way to start using MSAL is to make an acquireToken request using the new AcquireTokenParameter object and setting the resource ID value.

注意

不能同时设置范围和资源 ID。尝试同时设置两者会导致 IllegalArgumentExceptionIt's not possible to set both scopes and a resource id. Attempting to set both will result in an IllegalArgumentException.

这与使用 v1 时的行为相同。This will result in the same v1 behavior that you are used. 在应用注册中请求的所有权限是用户首次交互期间从用户请求的。All permissions requested in your app registration are requested from the user during their first interaction.

仅在有需要时才进行身份验证并请求权限Authenticate and request permissions only as needed

若要利用增量许可,请创建应用要在应用注册中使用的权限(范围)列表,并根据以下条件将其组织成两份列表:To take advantage of incremental consent, make a list of permissions (scopes) that your app uses from your app registration, and organize them into two lists based on:

  • 用户在登录期间首次与应用交互时你要请求的范围。Which scopes you want to request during the user's first interaction with your app during sign-in.
  • 与应用的某项重要功能关联的权限(你还需要向用户解释为何需要这些权限)。The permissions that are associated with an important feature of your app that you will also need to explain to the user.

组织范围后,请根据要请求其令牌的资源 (API) 组织每个列表。Once you've organized the scopes, organize each list by which resource (API) you want to request a token for. 以及同时希望用户授权的任何其他范围。As well as any other scopes that you wish the user to authorize at the same time.

用于向 MSAL 发出请求的参数对象支持:The parameters object used to make your request to MSAL supports:

  • Scope:要请求其授权并接收其访问令牌的范围列表。Scope: The list of scopes that you want to request authorization for and receive an access token.
  • ExtraScopesToConsent:在请求另一资源的访问令牌时要请求其授权的范围的附加列表。ExtraScopesToConsent: An additional list of scopes that you want to request authorization for while you're requesting an access token for another resource. 使用此范围列表可以最大程度地减少请求用户授权的次数。This list of scopes allows you to minimize the number of times that you need to request user authorization. 这意味着可以减少用户授权或许可提示的次数。Which means fewer user authorization or consent prompts.

从 AuthenticationContext 迁移到 PublicClientApplicationsMigrate from AuthenticationContext to PublicClientApplications

构造 PublicClientApplicationConstructing PublicClientApplication

使用 MSAL 时,将实例化 PublicClientApplicationWhen you use MSAL, you instantiate a PublicClientApplication. 此对象为应用标识建模,并用于向一个或多个颁发机构发出请求。This object models your app identity and is used to make requests to one or more authorities. 将使用此对象来配置客户端标识、重定向 URI、默认颁发机构、是使用设备浏览器还是嵌入式 Web 视图、日志级别,等等。With this object you will configure your client identity, redirect URI, default authority, whether to use the device browser vs. embedded web view, the log level, and more.

可以使用 JSON 以声明方式配置此对象,然后将其作为文件提供,或作为资源存储在 APK 中。You can declaratively configure this object with JSON, which you either provide as a file or store as a resource within your APK.

尽管此对象不是单一实例,但在内部,它使用共享的 Executors 来处理交互式请求和静默请求。Although this object is not a singleton, internally it uses shared Executors for both interactive and silent requests.

企业到企业Business to Business

在 ADAL 中,从中请求访问令牌的每个组织都需要 AuthenticationContext 的单独实例。In ADAL, every organization that you request access tokens from requires a separate instance of the AuthenticationContext. 在 MSAL 中,不再存在此项要求。In MSAL, this is no longer a requirement. 可以在静默请求或交互式请求中指定要从中请求令牌的授权机构。You can specify the authority from which you want to request a token as part of your silent or interactive request.

从颁发机构验证迁移到已知颁发机构Migrate from authority validation to known authorities

MSAL 不提供用于启用或禁用颁发机构验证的标志。MSAL does not have a flag to enable or disable authority validation. 颁发机构验证是 ADAL 以及早期版本的 MSAL 中的一项功能,用于防止代码从可能的恶意颁发机构请求令牌。Authority validation is a feature in ADAL, and in the early releases of MSAL, that prevents your code from requesting tokens from a potentially malicious authority. MSAL 现在会检索 Microsoft 已知的颁发机构列表,并将该列表与在配置中指定的颁发机构合并到一起。MSAL now retrieves a list of authorities known to Microsoft and merges that list with the authorities that you've specified in your configuration.

提示

对于 Azure 企业到消费者 (B2C) 用户而言,这意味着不再需要禁用颁发机构验证,If you're an Azure Business to Consumer (B2C) user, this means you no longer have to disable authority validation. 而可以将每个受支持的 Azure AD B2C 策略作为颁发机构包含在 MSAL 配置中。Instead, include each of the your supported Azure AD B2C policies as authorities in your MSAL configuration.

如果尝试使用 Microsoft 未知的颁发机构,并且未在配置中包含该颁发机构,将会收到 UnknownAuthorityExceptionIf you attempt to use an authority that isn't known to Microsoft, and isn't included in your configuration, you will get an UnknownAuthorityException.

日志记录Logging

现在可在配置中以声明方式配置日志记录,如下所示:You can now declaratively configure logging as part of your configuration, like this:

"logging": {
   "pii_enabled": false,
   "log_level": "WARNING",
   "logcat_enabled": true
 }

从 UserInfo 迁移到帐户Migrate from UserInfo to Account

在 ADAL 中,AuthenticationResult 提供一个 UserInfo 对象用于检索有关经过身份验证的帐户的信息。In ADAL, the AuthenticationResult provides a UserInfo object used to retrieve information about the authenticated account. 术语“用户”(表示某个人或软件代理)的运用方式使得难以传达某些应用支持具有多个帐户的单个用户(无论是人还是软件代理)这一信息。The term "user", which meant a human or software agent, was applied in a way that made it difficult to communicate that some apps support a single user (whether a human or software agent) that has multiple accounts.

以某个银行帐户为例。Consider a bank account. 你可能在多个金融机构建立了多个帐户。You may have more than one account at more than one financial institution. 建立每个帐户时,机构将你为(用户)颁发凭据(例如 ATM 卡和 PIN)用于访问余额、帐单付款等。When you open an account, you (the user) are issued credentials, such as an ATM Card & PIN, that are used to access your balance, bill payments, and so on, for each account. 这些凭据只能在颁发它们的金融机构使用。Those credentials can only be used at the financial institution that issued them.

与金融机构的帐户一样,Microsoft 标识平台中的帐户也是使用凭据访问的。By analogy, like accounts at a financial institution, accounts in the Microsoft identity platform are accessed using credentials. 这些凭据是在 Microsoft 注册的、由 Microsoft 颁发,Those credentials are either registered with, or issued by, Microsoft. 或者由 Microsoft 代表某家组织颁发。Or by Microsoft on behalf of an organization.

相比之下,Microsoft 标识平台与金融机构的不同之处在于,Microsoft 标识平台提供一个框架,可让用户使用一个帐户及其关联的凭据来访问属于多个个人和组织的资源。Where the Microsoft identity platform differs from a financial institution, in this analogy, is that the Microsoft identity platform provides a framework that allows a user to use one account, and its associated credentials, to access resources that belong to multiple individuals and organizations. 这类似于用户能够使用某家银行在另一所金融机构颁发的银行卡。This is like being able to use a card issued by one bank, at yet another financial institution. 这种运作方式之所以可行,是因为相关的所有组织都使用 Microsoft 标识平台,允许在多个组织中使用一个帐户。This works because all of the organizations in question are using the Microsoft identity platform, which allows one account to be used across multiple organizations. 下面是一个示例:Here's an example:

Sam 在 Contoso.com 任职,同时管理属于 Fabrikam.com 的 Azure 虚拟机。Sam works for Contoso.com but manages Azure virtual machines that belong to Fabrikam.com. 要使 Sam 能够管理 Fabrikam 的虚拟机,他需要获取访问这些虚拟机的授权。For Sam to manage Fabrikam's virtual machines, he needs to be authorized to access them. 要向 Sam 授予此访问权限,可将其帐户添加到 Fabrikam.com,并向其帐户授予一个可以管理虚拟机的角色。This access can be granted by adding Sam's account to Fabrikam.com, and granting his account a role that allows him to work with the virtual machines. 也可以使用 Azure 门户进行这种授权。This would be done with the Azure portal.

将 Sam 的 Contoso.com 帐户添加为 Fabrikam.com 的成员会导致在 Fabrikam.com 的 Azure Active Directory 中为 Sam 创建新的记录。Adding Sam's Contoso.com account as a member of Fabrikam.com would result in the creation of a new record in Fabrikam.com's Azure Active Directory for Sam. Sam 在 Azure Active Directory 中的记录称为用户对象。Sam's record in Azure Active Directory is known as a user object. 在这种情况下,该用户对象会指向回到 Sam 在 Contoso.com 中的用户对象。In this case, that user object would point back to Sam's user object in Contoso.com. Sam 的 Fabrikam 用户对象是 Sam 的本地表示形式,用于在 Fabrikam.com 的上下文中存储与 Sam 关联的帐户的相关信息。Sam's Fabrikam user object is the local representation of Sam, and would be used to store information about the account associated with Sam in the context of Fabrikam.com. 在 Contoso.com 中,Sam 的职称为“高级 DevOps 顾问”。In Contoso.com, Sam's title is Senior DevOps Consultant. 在 Fabrikam 中,Sam 的职称为“虚拟机签约管理员”。In Fabrikam, Sam's title is Contractor-Virtual Machines. 在 Contoso.com 中,Sam 既不负责,也未获授权管理虚拟机。In Contoso.com, Sam is not responsible, nor authorized, to manage virtual machines. 在 Fabrikam.com 中,这是他的唯一职责。In Fabrikam.com, that's his only job function. 但是,Sam 仍只有一组需要跟踪的凭据,即 Contoso.com 颁发的凭据。Yet Sam still only has one set of credentials to keep track of, which are the credentials issued by Contoso.com.

成功发出 acquireToken 调用后,将会看到对 IAccount 对象的引用,该对象可在后续的 acquireTokenSilent 请求中使用。Once a successful acquireToken call is made, you will see a reference to an IAccount object that can be used in later acquireTokenSilent requests.

IMultiTenantAccountIMultiTenantAccount

如果某个应用可以从代表某帐户的每个租户中访问有关该帐户的声明,则可以将 IAccount 对象强制转换为 IMultiTenantAccountIf you have an app that accesses claims about an account from each of the tenants in which the account is represented, you can cast IAccount objects to IMultiTenantAccount. 此接口提供按租户 ID 键控的 ITenantProfiles 映射,使你能够在相对于当前帐户的、从中请求了令牌的每个租户中访问属于该帐户的声明。This interface provides a map of ITenantProfiles, keyed by tenant ID, that allows you to access the claims that belong to the account in each of the tenants you've requested a token from, relative to the current account.

位于 IAccountIMultiTenantAccount 根位置的声明始终包含主租户中的声明。The claims at the root of the IAccount and IMultiTenantAccount always contain the claims from the home tenant. 如果你尚未对主租户中的令牌发出请求,则此集合是空的。If you have not yet made a request for a token within the home tenant, this collection will be empty.

其他更改Other changes

使用新的 AuthenticationCallbackUse the new AuthenticationCallback

// Existing ADAL Interface
public interface AuthenticationCallback<T> {

    /**
     * This will have the token info.
     *
     * @param result returns <T>
     */
    void onSuccess(T result);

    /**
     * Sends error information. This can be user related error or server error.
     * Cancellation error is AuthenticationCancelError.
     *
     * @param exc return {@link Exception}
     */
    void onError(Exception exc);
}
// New Interface for Interactive AcquireToken
public interface AuthenticationCallback {

    /**
     * Authentication finishes successfully.
     *
     * @param authenticationResult {@link IAuthenticationResult} that contains the success response.
     */
    void onSuccess(final IAuthenticationResult authenticationResult);

    /**
     * Error occurs during the authentication.
     *
     * @param exception The {@link MsalException} contains the error code, error message and cause if applicable. The exception
     *                  returned in the callback could be {@link MsalClientException}, {@link MsalServiceException}
     */
    void onError(final MsalException exception);

    /**
     * Will be called if user cancels the flow.
     */
    void onCancel();
}

// New Interface for Silent AcquireToken
public interface SilentAuthenticationCallback {

    /**
     * Authentication finishes successfully.
     *
     * @param authenticationResult {@link IAuthenticationResult} that contains the success response.
     */
    void onSuccess(final IAuthenticationResult authenticationResult);

    /**
     * Error occurs during the authentication.
     *
     * @param exception The {@link MsalException} contains the error code, error message and cause if applicable. The exception
     *                  returned in the callback could be {@link MsalClientException}, {@link MsalServiceException} or
     *                  {@link MsalUiRequiredException}.
     */
    void onError(final MsalException exception);
}


迁移到新的异常Migrate to the new exceptions

在 ADAL 中,有一个异常类型 AuthenticationException 包含用于检索 ADALError 枚举值的方法。In ADAL, there's one type of exception, AuthenticationException, which includes a method for retrieving the ADALError enum value. MSAL 中提供异常层次结构,每个异常具有自身的一组关联的特定错误代码。In MSAL, there's a hierarchy of exceptions, and each has its own set of associated specific error codes.

MSAL 异常列表List of MSAL Exceptions

异常Exception 说明Description
MsalException MSAL 引发的默认选择异常。Default checked exception thrown by MSAL.
MsalClientException 当错误在客户端上发生时引发。Thrown if the error is client side.
MsalArgumentException 当一个或多个输入参数无效时引发。Thrown if one or more inputs arguments are invalid.
MsalClientException 当错误在客户端上发生时引发。Thrown if the error is client side.
MsalServiceException 当错误在服务器端上发生时引发。Thrown if the error is server side.
MsalUserCancelException 当用户取消了身份验证流时引发。Thrown if the user canceled the authentication flow.
MsalUiRequiredException 当令牌无法以静默方式刷新时引发。Thrown if the token can't be refreshed silently.
MsalDeclinedScopeException 当服务器拒绝了一个或多个请求的范围时引发。Thrown if one or more requested scopes were declined by the server.
MsalIntuneAppProtectionPolicyRequiredException 当资源启用了 MAMCA 保护策略时引发。Thrown if the resource has MAMCA protection policy enabled.

ADALError 到 MsalException ErrorCodeADALError to MsalException ErrorCode

ADAL 日志记录到 MSAL 日志记录ADAL Logging to MSAL Logging

// Legacy Interface
    StringBuilder logs = new StringBuilder();
    Logger.getInstance().setExternalLogger(new ILogger() {
            @Override
            public void Log(String tag, String message, String additionalMessage, LogLevel logLevel, ADALError errorCode) {
                logs.append(message).append('\n');
            }
        });
// New interface
  StringBuilder logs = new StringBuilder();
  Logger.getInstance().setExternalLogger(new ILoggerCallback() {
            @Override
            public void log(String tag, Logger.LogLevel logLevel, String message, boolean containsPII) {
                logs.append(message).append('\n');
            }
        });

// New Log Levels:
public enum LogLevel
{
        /**
         * Error level logging.
         */
        ERROR,
        /**
         * Warning level logging.
         */
        WARNING,
        /**
         * Info level logging.
         */
        INFO,
        /**
         * Verbose level logging.
         */
        VERBOSE
}