在 Azure Active Directory B2C 中使用 MSAL4J 为 Java Tomcat 应用启用登录
本文演示了一个 Java Tomcat 应用程序,该应用程序使用 适用于 Java 的 Microsoft 身份验证库(MSAL4J)针对 Azure Active Directory B2C(Azure AD B2C)对用户进行身份验证。
下图显示了应用的拓扑:
应用使用 MSAL4J 登录用户并从 Azure AD B2C 获取 ID 令牌 。 ID 令牌证明用户已通过 Azure AD B2C 租户进行身份验证。
先决条件
- JDK 版本 8 或更高版本
- Maven 3
- Azure AD B2C 租户。 有关详细信息,请参阅 教程:创建 Azure Active Directory B2C 租户
- Azure AD B2C 租户中的用户帐户。
建议
- 一些熟悉 Java/Jakarta Servlets。
- 一些熟悉 Linux/OSX 终端或 Windows PowerShell。
- 用于检查令牌的 jwt.ms。
- 用于监视网络活动和故障排除的 Fiddler 。
- 按照 Microsoft Entra ID 博客了解最新开发的最新动态。
设置示例
以下部分演示如何设置示例应用程序。
克隆或下载示例存储库
若要克隆示例,请打开 Bash 窗口并使用以下命令:
git clone https://github.com/Azure-Samples/ms-identity-msal-java-samples.git
cd 3-java-servlet-web-app/1-Authentication/sign-in-b2c
或者,导航到 ms-identity-msal-java-samples 存储库,然后将其下载为 .zip 文件并将其提取到硬盘驱动器。
重要
若要避免 Windows 上的文件路径长度限制,请将存储库克隆或提取到硬盘驱动器根附近的目录中。
将示例应用程序注册到 Azure AD B2C 租户
该示例附带预注册的应用程序,用于测试目的。 若要使用自己的 Azure AD B2C 租户和应用程序,请按照以下部分中的步骤在Azure 门户中注册和配置应用程序。 否则,请继续执行运行示例的步骤。
选择要在其中创建应用程序的 Azure AD B2C 租户
若要选择租户,请使用以下步骤:
登录到 Azure 门户。
如果帐户存在于多个 Azure AD B2C 租户中,请选择Azure 门户角的配置文件,然后选择“切换目录”以将会话更改为所需的 Azure AD B2C 租户。
创建用户流和自定义策略
若要创建常见的用户流,例如注册、登录、配置文件编辑和密码重置,请参阅 教程:在 Azure Active Directory B2C 中创建用户流。
还应考虑 在 Azure Active Directory B2C 中创建自定义策略,但是,这超出了本教程的范围。
添加外部标识提供者
请参阅 教程:将标识提供者添加到 Azure Active Directory B2C 中的应用程序。
注册应用(ms-identity-b2c-java-servlet-webapp-authentication)
若要注册应用,请使用以下步骤:
导航到Azure 门户并选择 Azure AD B2C。
在导航窗格中选择 “应用注册 ”,然后选择“ 新建注册”。
在 显示的“注册应用程序”页 中,输入以下应用程序注册信息:
- 在“ 名称 ”部分中,输入一个有意义的应用程序名称,以便向应用的用户显示,例如
ms-identity-b2c-java-servlet-webapp-authentication
。 - 在“支持的帐户类型”下,选择任何组织目录中的帐户和个人Microsoft帐户(例如 Skype、Xbox、Outlook.com)。
- 在“重定向 URI”(可选)部分中,在组合框中选择“Web”,然后输入以下重定向 URI:
http://localhost:8080/ms-identity-b2c-java-servlet-webapp-authentication/auth_redirect
- 在“ 名称 ”部分中,输入一个有意义的应用程序名称,以便向应用的用户显示,例如
选择“注册”以创建应用程序。
在应用的注册页上,查找并复制 应用程序(客户端)ID 值以供以后使用。 在应用的配置文件或文件中使用此值。
选择“保存”以保存更改。
在应用的注册页上,选择 导航窗格中的“证书和机密 ”,打开可以生成机密并上传证书的页面。
在“客户端密码”部分中,选择“新建客户端密码” 。
键入说明 - 例如应用 机密。
选择可用持续时间之一: 1 年、 2 年内或 永不过期。
选择 添加 。 将显示生成的值。
复制并保存生成的值,以便在后续步骤中使用。 代码的配置文件需要此值。 此值不会再次显示,不能通过任何其他方式检索该值。 因此,在导航到任何其他屏幕或窗格之前,请务必将其从Azure 门户保存。
配置应用(ms-identity-b2c-java-servlet-webapp-authentication)以使用应用注册
使用以下步骤配置应用:
注意
在以下步骤中, ClientID
与 Application ID
或 AppId
相同。
在 IDE 中打开项目。
打开 ./src/main/resources/authentication.properties 文件。
找到该
aad.clientId
属性,并将现有值替换为应用程序 ID 或clientId
ms-identity-b2c-java-servlet-webapp-authentication
Azure 门户中的应用程序。找到该属性,并将
aad.secret
现有值替换为在从Azure 门户创建ms-identity-b2c-java-servlet-webapp-authentication
应用程序期间保存的值。找到该属性,并将
aad.scopes
现有应用程序 clientId 替换为在本部分的步骤 1 中放置aad.clientId
的值。找到该
aad.authority
属性,并将第一个实例fabrikamb2c
替换为在Azure 门户中创建ms-identity-b2c-java-servlet-webapp-authentication
应用程序的 Azure AD B2C 租户的名称。找到该
aad.authority
属性,并将第二个实例fabrikamb2c
替换为在其中在Azure 门户中创建ms-identity-b2c-java-servlet-webapp-authentication
应用程序的 Azure AD B2C 租户的名称。找到该
aad.signInPolicy
属性,并将其替换为在 azure AD B2C 租户中创建的注册/登录用户流策略的名称,在该租户ms-identity-b2c-java-servlet-webapp-authentication
中创建应用程序Azure 门户。找到该
aad.passwordResetPolicy
属性,并将其替换为在 azure AD B2C 租户ms-identity-b2c-java-servlet-webapp-authentication
中创建的应用程序在Azure 门户中创建的应用程序的密码重置用户流策略的名称。找到该
aad.editProfilePolicy
属性,并将其替换为在 azure AD B2C 租户中创建的编辑配置文件用户流策略的名称,在该租户ms-identity-b2c-java-servlet-webapp-authentication
中创建Azure 门户中的应用程序。
生成示例
若要使用 Maven 生成示例,请导航到包含 示例pom.xml 文件的目录,然后运行以下命令:
mvn clean package
此命令生成可在 各种应用程序服务器上运行的 .war 文件。
运行示例
以下部分演示如何将示例部署到Azure App 服务。
先决条件
-
如果 Maven 不是首选开发工具,请参阅以下使用其他工具的类似教程:
配置 Maven 插件
部署到Azure App 服务时,部署会自动使用 Azure CLI 中的 Azure 凭据。 如果未在本地安装 Azure CLI,则 Maven 插件会使用 OAuth 或设备登录来进行身份验证。 有关详细信息,请参阅 Maven 插件的身份验证。
使用以下步骤配置插件:
运行以下命令来配置部署。 此命令可帮助你设置Azure App 服务操作系统、Java 版本和 Tomcat 版本。
mvn com.microsoft.azure:azure-webapp-maven-plugin:2.12.0:config
对于 “创建新的运行配置”,请按 Y,然后按 Enter。
对于 OS 的“定义”值,请按 1 for Windows 或 2 for Linux,然后按 Enter。
对于 javaVersion 的定义值,请按 2 for Java 11,然后按 Enter。
对于 WebContainer 的“定义”值,请按 Tomcat 9.0 的 4 ,然后按 Enter。
对于 “定义 pricingTier”值,请按 Enter 选择默认 的 P1v2 层。
对于 “确认”,请按 Y,然后按 Enter。
以下示例显示了部署过程的输出:
Please confirm webapp properties
AppName : msal4j-servlet-auth-1707209552268
ResourceGroup : msal4j-servlet-auth-1707209552268-rg
Region : centralus
PricingTier : P1v2
OS : Linux
Java Version: Java 11
Web server stack: Tomcat 9.0
Deploy to slot : false
Confirm (Y/N) [Y]: [INFO] Saving configuration to pom.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 37.112 s
[INFO] Finished at: 2024-02-06T08:53:02Z
[INFO] ------------------------------------------------------------------------
确认选择后,该插件会将所需的插件元素和设置添加到项目的pom.xml文件中,以将应用配置为在Azure App 服务中运行。
pom.xml文件的相关部分应类似于以下示例:
<build>
<plugins>
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>>azure-webapp-maven-plugin</artifactId>
<version>x.xx.x</version>
<configuration>
<schemaVersion>v2</schemaVersion>
<resourceGroup>your-resourcegroup-name</resourceGroup>
<appName>your-app-name</appName>
...
</configuration>
</plugin>
</plugins>
</build>
可以直接在pom.xml中修改App 服务的配置。 下表列出了一些常见配置:
properties | 必选 | 说明 |
---|---|---|
subscriptionId |
false | 订阅的 ID。 |
resourceGroup |
是 | 应用的 Azure 资源组。 |
appName |
是 | 应用的名称。 |
region |
false | 托管应用的区域。 默认值为 centralus 。 有关有效区域,请参阅 支持的区域。 |
pricingTier |
false | 应用的定价层。 默认值为 P1v2 生产工作负荷。 Java 开发和测试的建议最小值是 B2 。 有关详细信息,请参阅App 服务定价。 |
runtime |
false | 运行时环境配置。 有关详细信息,请参阅配置详细信息。 |
deployment |
false | 部署配置。 有关详细信息,请参阅配置详细信息。 |
有关配置的完整列表,请参阅插件参考文档。 所有 Azure Maven 插件共享一组常见的配置。 有关这些配置,请参阅 常见配置。 有关特定于Azure App 服务的配置,请参阅 Azure 应用:配置详细信息。
请务必保存保留这些 appName
值, resourceGroup
供以后使用。
准备应用进行部署
将应用程序部署到App 服务时,重定向 URL 将更改为已部署的应用实例的重定向 URL。 使用以下步骤更改属性文件中的这些设置:
导航到应用的 authentication.properties 文件并更改已部署应用的域名的值
app.homePage
,如以下示例所示。 例如,如果在example-domain
上一步中选择了应用名称,则现在必须用于https://example-domain.azurewebsites.net
app.homePage
该值。 请确保也已将协议从http
此更改为https
。# app.homePage is by default set to dev server address and app context path on the server # for apps deployed to azure, use https://your-sub-domain.azurewebsites.net app.homePage=https://<your-app-name>.azurewebsites.net
保存此文件后,使用以下命令重新生成应用:
mvn clean package
重要
在此相同的 authentication.properties 文件中,你有一个设置 aad.secret
。 最好将此值部署到App 服务。 在代码中保留此值并可能将其推送到 git 存储库,这两种做法都不是很好的做法。 若要从代码中删除此机密值,可以在“部署到 App 服务 - 删除机密”部分中找到更详细的指导。 本指南增加了将机密值推送到密钥库并使用密钥库引用的额外步骤。
更新Microsoft Entra ID 应用注册
由于重定向 URI 更改为已部署的应用Azure App 服务,因此还需要更改Microsoft Entra ID 应用注册中的重定向 URI。 若要进行此更改,请使用以下步骤:
导航到面向开发人员的 Microsoft 标识平台应用注册页。
使用搜索框搜索应用注册 ,例如
java-servlet-webapp-authentication
。通过选择应用名称打开应用注册。
从菜单中选择“身份验证”。
在“Web - 重定向 URI”部分中,选择“添加 URI”。
填写应用的 URI,追加
/auth/redirect
- 例如。https://<your-app-name>.azurewebsites.net/auth/redirect
选择“保存”。
部署应用
现在可以将应用部署到Azure App 服务。 使用以下命令确保登录到 Azure 环境以执行部署:
az login
在pom.xml文件中准备好所有配置后,现在可以使用以下命令将 Java 应用部署到 Azure:
mvn package azure-webapp:deploy
部署完成后,应用程序已 http://<your-app-name>.azurewebsites.net/
准备就绪。 使用本地 Web 浏览器打开 URL,此时应会看到应用程序的起始页 msal4j-servlet-auth
。
探索示例
使用以下步骤浏览示例:
- 请注意屏幕中心显示的已登录或注销状态。
- 选择角落中的上下文敏感按钮。 首次运行应用时,此按钮将 读取登录 。
- 在下一页上,按照说明使用所选标识提供者的帐户登录。
- 请注意,上下文敏感按钮现在显示 “注销 ”并显示用户名。
- 选择 ID 令牌详细信息 以查看某些 ID 令牌的解码声明。
- 还可以选择编辑配置文件。 选择链接可编辑详细信息,例如显示名称、居住地和职业。
- 使用角落中的按钮注销。
- 注销后,导航到令牌详细信息页的以下 URL:
http://localhost:8080/ms-identity-b2c-java-servlet-webapp-authentication/auth_token_details
在这里,可以观察应用如何显示401: unauthorized
错误而不是 ID 令牌声明。
关于代码
此示例演示如何使用 MSAL4J 将用户登录到 Azure AD B2C 租户。
目录
下表显示了示例项目文件夹的内容:
文件/文件夹 | 说明 |
---|---|
AuthHelper.java | 用于身份验证的帮助程序函数。 |
Config.java | 在启动时运行,并配置属性读取器和记录器。 |
authentication.properties | Microsoft Entra ID 和程序配置。 |
AuthenticationFilter.java | 将未经身份验证的请求重定向到受保护的资源到 401 页。 |
MsalAuthSession | 使用 .HttpSession 将所有与 MSAL 相关的会话属性存储在会话属性中。 |
____Servlet.java | 所有可用的终结点在以 ____Servlet.java 结尾的.java类中定义。 |
CHANGELOG.md | 示例更改列表。 |
CONTRIBUTING.md | 参与示例的指南。 |
许可证 | 示例的许可证。 |
ConfidentialClientApplication
ConfidentialClientApplication
实例在AuthHelper.java文件中创建,如以下示例所示。 此对象有助于创建 Azure AD B2C 授权 URL,并有助于交换访问令牌的身份验证令牌。
IClientSecret secret = ClientCredentialFactory.createFromSecret(SECRET);
confClientInstance = ConfidentialClientApplication
.builder(CLIENT_ID, secret)
.b2cAuthority(AUTHORITY + policy)
.build();
以下参数用于实例化:
- 应用的客户端 ID。
- 客户端机密,这是机密客户端应用程序的要求。
- Azure AD B2C 颁发机构与相应的
UserFlowPolicy
注册、登录、配置文件编辑或密码重置连接。
在此示例中,使用 Config.java 文件中的属性读取器从 authentication.properties 文件中读取这些值。
分步演练
以下步骤提供应用的功能的演练:
登录过程的第一步是向 Azure Active Directory B2C 租户的终结点发送请求
/authorize
。 MSAL4JConfidentialClientApplication
实例用于构造授权请求 URL,应用将浏览器重定向到此 URL,如以下示例所示:final ConfidentialClientApplication client = getConfidentialClientInstance(policy); final AuthorizationRequestUrlParameters parameters = AuthorizationRequestUrlParameters .builder(REDIRECT_URI, Collections.singleton(SCOPES)).responseMode(ResponseMode.QUERY) .prompt(Prompt.SELECT_ACCOUNT).state(state).nonce(nonce).build(); final String redirectUrl = client.getAuthorizationRequestUrl(parameters).toString(); Config.logger.log(Level.INFO, "Redirecting user to {0}", redirectUrl); resp.setStatus(302); resp.sendRedirect(redirectUrl);
以下列表描述了此代码的功能:
AuthorizationRequestUrlParameters
:必须设置的参数才能生成 AuthorizationRequestUrl。REDIRECT_URI
:收集用户凭据后,Azure AD B2C 会重定向浏览器以及身份验证代码。SCOPES
: 范围 是应用程序请求的权限。通常,这三个范围
openid profile offline_access
足以接收 ID 令牌响应。 但是,MSAL4J 要求来自 Azure AD B2C 的所有响应也包含访问令牌。为了使 Azure AD B2C 分配访问令牌和 ID 令牌,请求必须包含额外的资源范围。 由于此应用实际上不需要外部资源范围,因此它将自己的客户端 ID 添加为第四个范围,以便接收访问令牌。
可以在 authentication.properties 文件中找到应用请求的范围的完整列表。
ResponseMode.QUERY
:Azure AD B2C 可以将响应作为 HTTP POST 请求中的表单参数或 HTTP GET 请求中的查询字符串参数返回。Prompt.SELECT_ACCOUNT
:Azure AD B2C 应要求用户选择要对其进行身份验证的帐户。state
:应用在每个令牌请求上设置的唯一变量,并在收到相应的 Azure AD B2C 重定向回调后销毁。 状态变量可确保从来自此应用和此会话的 Azure AD B2C 授权请求实际发出 Azure AD B2C 请求/auth_redirect endpoint
,从而防止 CSRF 攻击。 此操作在 AADRedirectServlet.java 文件中完成。nonce
:应用在每个令牌请求的会话中设置的唯一变量,并在收到相应的令牌后销毁。 此 nonce 将转录为分配了 Azure AD B2C 的结果令牌,从而确保没有发生令牌重播攻击。
Azure Active Directory B2C 会显示用户登录提示。 如果登录尝试成功,则用户的浏览器将重定向到应用的重定向终结点。 对此终结点的有效请求包含 授权代码。
然后,该
ConfidentialClientApplication
实例将此授权代码交换为 Azure Active Directory B2C 中的 ID 令牌和访问令牌,如以下示例所示:final AuthorizationCodeParameters authParams = AuthorizationCodeParameters .builder(authCode, new URI(REDIRECT_URI)) .scopes(Collections.singleton(SCOPES)).build(); final ConfidentialClientApplication client = AuthHelper .getConfidentialClientInstance(policy); final Future<IAuthenticationResult> future = client.acquireToken(authParams); final IAuthenticationResult result = future.get();
以下列表描述了此代码的功能:
AuthorizationCodeParameters
:必须设置的参数,才能交换 ID 和/或访问令牌的授权代码。authCode
:在重定向终结点收到的授权代码。REDIRECT_URI
:上一步中使用的重定向 URI 必须再次传递。SCOPES
:上一步中使用的范围必须再次传递。
如果
acquireToken
成功,则会提取令牌声明,并针对会话中存储的 nonce 验证 nonce 声明,如以下示例所示:parseJWTClaimsSetAndStoreResultInSession(msalAuth, result, serializedTokenCache); validateNonce(msalAuth) processSuccessfulAuthentication(msalAuth);
如果成功验证 nonce,身份验证状态将置于服务器端会话中,并利用类公开
MsalAuthSession
的方法,如以下示例所示:msalAuth.setAuthenticated(true); msalAuth.setUsername(msalAuth.getIdTokenClaims().get("name"));
详细信息
- 什么是 Azure Active Directory B2C?
- 可在 Active Directory B2C 中使用的应用程序类型
- 适用于 Azure Active Directory B2C 的建议和最佳做法
- Azure AD B2C 会话
- 适用于 Java 的 Microsoft 身份验证库 (MSAL)
有关 OAuth 2.0 协议在此方案中的工作方式和其他方案的详细信息,请参阅 Microsoft Entra ID 的身份验证方案。
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈