启用 Java Spring Boot 应用以登录用户并访问 Microsoft Graph

本文演示了一个 Java Spring Boot Web 应用,该应用可让用户登录并获取用于调用 Microsoft Graph 的访问令牌。 它使用适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库来进行身份验证、授权和令牌获取。 它使用适用于 Java 的 Microsoft Graph SDK 从 Graph 获取数据。

下图显示了应用的拓扑结构:

显示了应用拓扑结构的示意图。

该应用使用适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库从 Microsoft Entra ID 获取 Microsoft Graph访问令牌。 访问令牌证明用户已获授权访问范围中定义的 Microsoft Graph API 终结点。

先决条件

建议

设置示例

以下各部分将介绍如何设置示例应用程序。

克隆或下载示例存储库

要克隆示例,请打开 Bash 窗口并使用以下命令:

git clone https://github.com/Azure-Samples/ms-identity-msal-java-samples.git
cd 4-spring-web-app/2-Authorization-I/call-graph

或者,导航到 ms-identity-msal-java-samples 存储库,然后将其作为 .zip 文件下载并提取到硬盘驱动器。

重要

为避免 Windows 系统对文件路径长度的限制,请将存储库克隆提取到硬盘驱动器根目录附近。

使用 Microsoft Entra ID 租户注册示例应用程序

此示例中有一个项目。 以下各部分将介绍如何使用 Azure 门户注册应用。

选择要在其中创建应用程序的 Microsoft Entra ID 租户

要选择租户,请按以下步骤操作:

  1. 登录到 Azure 门户

  2. 如果帐户存在于多个 Microsoft Entra ID 租户中,请在 Azure 门户的角落里选择配置文件,然后选择“切换目录”,将会话更改为所需的 Microsoft Entra ID 租户。

注册应用 (java-spring-webapp-call-graph)

要注册应用,请按照以下步骤操作:

  1. 导航到 Azure 门户,然后选择 Microsoft Entra ID

  2. 在导航窗格中选择“应用注册”,然后选择“新注册”。

  3. 在出现的“注册应用程序”页面中,输入以下应用程序注册的信息:

    • 在“名称”部分中,输入一个有意义的应用名称,以便向应用用户显示,例如 java-spring-webapp-call-graph
    • 在“支持的帐户类型”下,选择“仅此组织目录中的帐户”。
    • 在“重定向 URI(可选)”部分的组合框中选择“Web”,然后输入以下重定向 URI:http://localhost:8080/login/oauth2/code/
  4. 选择“注册”以创建应用程序。

  5. 在应用的注册页面上,找到并复制“应用程序(客户端) ID”值,以供稍后使用。 可以在应用程序的一个或多个配置文件中使用此值。

  6. 在应用的注册页面上,选择导航窗格中的“证书和机密”,打开可生成机密和上传证书的页面。

  7. 在“客户端密码”部分中,选择“新建客户端密码”。

  8. 键入描述,例如“应用机密”。

  9. 从可用期限中选择一个:“1 年内”、“2 年内”或“永不过期”。

  10. 选择 添加 。 此时将显示生成的值。

  11. 复制并保存生成的值,以供在之后的步骤中使用。 代码的配置文件需要使用此值。 此值不会再显示,也无法通过任何其他方式获取。 因此,在导航到任何其他屏幕或窗格之前,请务必从 Azure 门户保存该值。

  12. 在应用程序的注册页面上,从导航窗格中选择“API 权限”窗格,以便打开对应用程序所需 API 的访问权限的页面。

  13. 选择“添加权限”,然后确保选择“Microsoft API”选项卡。

  14. 在“常用 Microsoft API”部分,选择“Microsoft Graph”。

  15. 在“委托的权限”部分中,从列表中选择“User.Read”。 如有必要,请使用搜索框。

  16. 选择“添加权限”。


配置应用 (java-spring-webapp-call-graph) 以使用应用注册

按照以下步骤来配置应用:

注意

在以下步骤中,ClientIDApplication IDAppId 相同。

  1. 在 IDE 中打开项目。

  2. 打开 src\main\resources\application.yml 文件。

  3. 找到占位符 Enter_Your_Tenant_ID_Here,用你的 Microsoft Entra 租户 ID 替换现有值。

  4. 找到占位符 Enter_Your_Client_ID_Here,然后用从 Azure 门户复制的 clientId 应用的应用程序 ID 或 java-spring-webapp-call-graph 替换现有值。

  5. 找到占位符 Enter_Your_Client_Secret_Here,然后用从 Azure 门户复制的创建 java-spring-webapp-call-graph 时保存的值替换现有值。

运行示例

以下各部分将展示如何将示例部署到 Azure 容器应用。

先决条件

准备 Spring 项目

使用以下步骤来准备项目:

  1. 使用以下 Maven 命令生成项目:

    mvn clean verify
    
  2. 使用以下命令在本地运行示例项目:

    mvn spring-boot:run
    

安装

要从 CLI 登录到 Azure,请运行以下命令,然后按照提示完成身份验证过程。

az login

为了确保运行最新版本的 CLI,请运行升级命令。

az upgrade

接下来,安装或更新适用于 CLI 的 Azure 容器应用扩展。

如果在 Azure CLI 中运行 az containerapp 命令时收到有关缺少参数的错误,请确保已安装最新版本的 Azure 容器应用扩展。

az extension add --name containerapp --upgrade

注意

从 2024 年 5 月开始,Azure CLI 扩展不再默认启用预览功能。 要访问容器应用预览功能,请使用 --allow-preview true 安装容器应用扩展。

az extension add --name containerapp --upgrade --allow-preview true

现在已安装当前扩展或模块,接下来请注册 Microsoft.AppMicrosoft.OperationalInsights 命名空间。

注意

Azure 容器应用资源已从 Microsoft.Web 命名空间迁移到 Microsoft.App 命名空间。 有关详细信息,请参阅 2022 年 3 月从 Microsoft.Web 到 Microsoft.App 的命名空间迁移

az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights

创建 Azure 容器应用环境

完成 Azure CLI 安装后,接下来可以定义要在本文中使用的环境变量。

在 bash shell 中定义以下变量。

export RESOURCE_GROUP="ms-identity-containerapps"
export LOCATION="canadacentral"
export ENVIRONMENT="env-ms-identity-containerapps"
export API_NAME="ms-identity-api"
export JAR_FILE_PATH_AND_NAME="./target/ms-identity-spring-boot-webapp-0.0.1-SNAPSHOT.jar"

创建资源组。

az group create  \
    --name $RESOURCE_GROUP \
    --location $LOCATION \

使用自动生成的日志分析工作区创建环境。

az containerapp env create \
    --name $ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

显示容器应用环境的默认域。 记下此域,以便在后面的部分中使用。

az containerapp env show \
    --name $ENVIRONMENT \
    --resource-group $RESOURCE_GROUP \
    --query properties.defaultDomain

准备部署应用程序

将应用程序部署到 Azure 容器应用时,重定向 URL 会更改为 Azure 容器应用中已部署应用程序实例的重定向 URL。 使用以下步骤更改 application.yml 文件中的这些设置:

  1. 导航到应用程序的 src\main\resources\application.yml 文件,并将 post-logout-redirect-uri 的值更改为已部署应用的域名,如下例所示。 请务必用实际值替换 <API_NAME><default-domain-of-container-app-environment>。 例如,使用上一步中 Azure 容器应用环境的默认域和应用名称 ms-identity-api,将使用 https://ms-identity-api.<default-domain> 作为 post-logout-redirect-uri 的值。

    post-logout-redirect-uri: https://<API_NAME>.<default-domain-of-container-app-environment>
    
  2. 保存此文件后,使用以下命令重新生成应用:

    mvn clean package
    

重要

应用程序的 application.yml 文件当前在 client-secret 参数中保存了客户机密的值。 在此文件中保留此值并非良好做法。 如果将文件提交到 Git 存储库,则也可能面临风险。 有关建议的方法,请参阅管理 Azure 容器应用中的机密

更新 Microsoft Entra ID 应用注册

由于重定向 URI 会更改为在 Azure 容器应用上部署的应用,因此还需要更改 Microsoft Entra ID 应用注册中的重定向 URI。 要进行此更改,请使用以下步骤:

  1. 导航到面向开发人员的 Microsoft 标识平台应用注册页

  2. 使用搜索框搜索应用注册,例如 java-servlet-webapp-authentication

  3. 选择应用名称,打开应用注册。

  4. 从菜单中选择“身份验证”。

  5. Web - 重定向 URI 部分中,选择“添加 URI”。

  6. 填写应用程序的 URI,附加 /login/oauth2/code/ - 例如 https://<containerapp-name>.<default domain of container app environment>/login/oauth2/code/

  7. 选择“保存”。

部署应用

将 JAR 包部署到 Azure 容器应用。

注意

如有必要,可以在 Java 生成环境变量中指定 JDK 版本。 有关详细信息,请参阅 在 Azure 容器应用中为 Java 生成环境变量

现在,可以使用 CLI 命令 az containerapp up 部署 WAR 文件。

az containerapp up \
    --name $API_NAME \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION \
    --environment $ENVIRONMENT \
    --artifact <JAR_FILE_PATH_AND_NAME> \
    --ingress external \
    --target-port 8080 \
    --query properties.configuration.ingress.fqdn

注意

默认的 JDK 版本为 17。 如果需要更改 JDK 版本以与应用程序兼容,可以使用 --build-env-vars BP_JVM_VERSION=<YOUR_JDK_VERSION> 参数来调整版本号。

有关生成环境变量的详细信息,请参阅 在 Azure 容器应用中为 Java 生成环境变量

验证应用

在此示例中,containerapp up 命令包含 --query properties.configuration.ingress.fqdn 参数,该参数会返回完全限定的域名 (FQDN),也称为应用的 URL。 按以下步骤检查应用程序的日志,以调查任何部署问题:

  1. 从“部署”部分的“输出”页面访问输出应用程序 URL。

  2. 在 Azure 容器应用实例“概述”页面的导航窗格中,选择“日志”以检查应用的日志。

探索示例

按照以下步骤来探索示例:

  1. 注意屏幕中央显示的登录或退出状态。
  2. 选择角落里的上下文相关按钮。 首次运行应用时,此按钮显示“登录”。 或者,选择令牌详细信息调用图形。 由于此页面受保护并需要身份验证,因此你会被自动重定向到登录页面。
  3. 在下一页上,按照说明使用 Microsoft Entra ID 租户中的帐户登录。
  4. 在同意屏幕上,请注意正在请求的范围。
  5. 成功完成登录流后,你将被重定向到主页(显示“登录状态”)或其他页面之一,这取决于哪个按钮触发了你的登录流。
  6. 请注意,上下文相关按钮现在显示“注销”并显示你的用户名。
  7. 如果是在主页上,请选择“ID 令牌详细信息”,以便查看 ID 令牌的一些已解码声明。
  8. 选择“调用图形”以调用 Microsoft Graph 的 /me 终结点,并查看所获取的一系列用户详细信息。
  9. 使用角落的按钮注销。状态页面会显示新的状态。

关于代码

本示例演示了如何使用适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库将用户登录到 Microsoft Entra ID 租户,以及获取用于调用 Microsoft Graph 的访问令牌。 此示例还使用了 Spring Oauth2 Client 和 Spring Web Boot Starter。

目录

下表列出了示例项目文件夹的内容:

文件/文件夹 说明
pom.xml 应用程序依赖项。
src/main/resources/templates/ 适用于 UI 的 Thymeleaf 模板。
src/main/resources/application.yml 应用程序和 Microsoft Entra ID Boot Starter 库配置。
src/main/java/com/microsoft/azuresamples/msal4j/msidentityspringbootwebapp/ 此目录包含主应用程序入口点、控制器和配置类。
.../MsIdentitySpringBootWebappApplication.java 主类。
.../SampleController.java 带有终结点映射的控制器。
.../SecurityConfig.java 安全配置 - 例如,哪些路由需要身份验证。
.../Utilities.java 实用工具类 - 例如,筛选器 ID 令牌声明。
CHANGELOG.md 示例更改的列表。
CONTRIBUTING.md 参与示例的指南。
许可证 示例的许可证。

ID 令牌声明

为了提取令牌详细信息,应用在请求映射中使用了 Spring Security 的 AuthenticationPrincipalOidcUser 对象,如下例所示。 有关此应用如何使用 ID 令牌声明的全部详情,请参阅示例控制器

import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
//...
@GetMapping(path = "/some_path")
public String tokenDetails(@AuthenticationPrincipal OidcUser principal) {
    Map<String, Object> claims = principal.getIdToken().getClaims();
}

对于登录,应用会向 Microsoft Entra ID Spring Boot Starter Java 客户端库自动配置的 Microsoft Entra ID 登录终结点发出请求,如下例所示:

<a class="btn btn-success" href="/oauth2/authorization/azure">Sign In</a>

对于注销,应用会向 logout 终结点发出 POST 请求,如下例所示:

<form action="#" th:action="@{/logout}" method="post">
  <input class="btn btn-warning" type="submit" value="Sign Out" />
</form>

依赖于身份验证的 UI 元素

该应用在 UI 模板页面中设置了一些简单的逻辑,用于根据用户是否通过身份验证来确定要显示的内容,如下面使用 Spring Security Thymeleaf 标签的示例所示:

<div sec:authorize="isAuthenticated()">
  this content only shows to authenticated users
</div>
<div sec:authorize="isAnonymous()">
  this content only shows to not-authenticated users
</div>

使用 AADWebSecurityConfigurerAdapter 保护路由

默认情况下,应用会保护 ID 令牌详细信息调用图形页面,因此只有已登录的用户才能访问这些页面。 应用会通过 app.protect.authenticated 文件中的 属性来配置这些路由。 要配置应用的特定需求,可以在其中一个类中扩展 AADWebSecurityConfigurationAdapter。 例如,请参阅此应用程序的 SecurityConfig 类,如以下代码所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends AADWebSecurityConfigurerAdapter{
  @Value( "${app.protect.authenticated}" )
  private String[] protectedRoutes;

    @Override
    public void configure(HttpSecurity http) throws Exception {
    // use required configuration form AADWebSecurityAdapter.configure:
    super.configure(http);
    // add custom configuration:
    http.authorizeRequests()
      .antMatchers(protectedRoutes).authenticated()     // limit these pages to authenticated users (default: /token_details, /call_graph)
      .antMatchers("/**").permitAll();                  // allow all other routes.
    }
}

调用图形

当用户导航到 /call_graph 时,应用程序会使用 Microsoft Entra ID 启动器准备的 GraphServiceClientOauth2AuthorizedClient 来创建 graphAuthorizedClient 实例。 应用会要求 GraphServiceClient 调用 /me 端点,并显示当前已登录用户的详细信息。 来自GraphServiceClient

Oauth2AuthorizedClient 必须使用正确的范围进行准备。 请参阅 application.yml 文件和以下“范围”部分。 如下例所示,Oauth2AuthorizedClient 用于显示访问令牌,并将其放在 Authorization 请求的 GraphServiceClient 标头中:

//see SampleController.java
@GetMapping(path = "/call_graph")
public String callGraph(@RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graphAuthorizedClient) {
  // See the Utilities.graphUserProperties() method for the full example of the following operation:
  GraphServiceClient graphServiceClient = Utilities.getGraphServiceClient(graphAuthorizedClient);
  User user = graphServiceClient.me().buildRequest().get();
  return user.displayName;
}

以下来自 application.yml 文件的示例显示了所请求的范围:

# see application.yml file
authorization-clients:
  graph:
    # Specifies the Microsoft Graph scopes that your app needs access to:
    scopes: https://graph.microsoft.com/User.Read

作用域

范围表明了 Microsoft Entra ID 应用程序请求的访问级别。 有关此应用程序请求的 Microsoft Graph 范围,请参阅 application.yml

默认情况下,应用程序会将范围值设置为 https://graph.microsoft.com/User.ReadUser.Read 范围用于从 /me 终结点 访问当前已登录用户的信息。 向 /me 终结点提出的有效请求必须包含 User.Read 范围。

在用户登录时,Microsoft Entra ID 会根据应用程序请求的范围向用户显示同意对话框。 如果用户同意一个或多个范围并获取令牌,则同意的范围会被编码到生成的访问令牌中。

在此应用中,graphAuthorizedClient 会显示访问令牌,用以证明用户同意访问哪些范围。 应用使用此令牌来创建用于处理 Graph 请求的 GraphServiceClient 实例。

通过使用 GraphServiceClient.me().buildRequest().get() 可生成请求并向 https://graph.microsoft.com/v1.0/me 提出。 访问令牌会被放在请求的 Authorization 标头中。

详细信息

有关 OAuth 2.0 协议如何在此方案和其他方案中工作的详细信息,请参阅 Microsoft Entra ID 的身份验证方案