使用 Microsoft Entra ID 保护 Java Spring Boot 应用

本文演示了一个 Java Spring Boot Web 应用,该应用使用适用于 JavaMicrosoft Entra ID Spring Boot Starter 客户端库在 Microsoft Entra ID 租户上登录用户。 它使用 OpenID 连接协议。

下图显示了应用的拓扑:

显示应用的拓扑的关系图。

客户端应用使用适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库登录用户并从 Microsoft Entra ID 获取 ID 令牌。 ID 令牌证明用户使用 Microsoft Entra ID 进行身份验证,并使用户能够访问受保护的路由。

先决条件

建议

设置示例

以下部分演示如何设置示例应用程序。

克隆或下载示例存储库

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

git clone https://github.com/Azure-Samples/ms-identity-java-spring-tutorial.git
cd ms-identity-java-spring-tutorial
cd 1-Authentication/sign-in

或者,导航到 ms-identity-java-spring-tutorial 存储库,然后将其下载为 .zip 文件并将其解压缩到硬盘驱动器。

重要

为了避免 Windows 上的路径长度限制,建议克隆到驱动器根附近的目录中。

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

此示例中有一个项目。 若要在Azure 门户上注册应用,可以按照手动配置步骤或使用 PowerShell 脚本。 该脚本执行以下任务:

  • 创建 Microsoft Entra ID 应用程序和相关对象,例如密码、权限和依赖项。
  • 修改项目配置文件。
  • 默认情况下,设置仅适用于组织目录中的帐户的应用程序。

使用以下步骤运行 PowerShell 脚本:

  1. 在 Windows 上,打开 PowerShell 并导航到克隆目录的根目录。

  2. 使用以下命令设置 PowerShell 的执行策略:

    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
    
  3. 使用以下命令运行配置脚本:

    cd .\AppCreationScripts\
    .\Configure.ps1
    

    注意

    应用创建脚本介绍了运行脚本的其他方法。 这些脚本还提供了自动化应用程序注册、配置和删除指南,有助于 CI/CD 方案。

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

使用以下步骤配置应用:

注意

在以下步骤中, 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 门户复制的应用程序 ID 或java-spring-webapp-authclientId应用。

  5. 找到占位符Enter_Your_Client_Secret_Here,并将现有值替换为在创建java-spring-webapp-auth从Azure 门户复制期间保存的值。

运行示例

以下部分介绍如何将示例部署到 Azure Spring Apps。

先决条件

准备 Spring 项目

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

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

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

    mvn spring-boot:run
    

配置 Maven 插件

在项目的根目录中运行以下命令,使用适用于 Azure Spring AppsMaven 插件配置应用:

mvn com.microsoft.azure:azure-spring-apps-maven-plugin:1.19.0:config

以下列表描述了命令交互:

  • OAuth2 登录名:需要根据 OAuth2 协议授权登录到 Azure。
  • 选择订阅:选择要在其中创建 Azure Spring Apps 实例的订阅列表编号,该实例默认为列表中的第一个订阅。 如果要使用默认数字,请按 Enter
  • 输入 Azure Spring Apps 名称:输入要创建的 Spring 应用实例的名称。 如果要使用默认名称,请按 Enter
  • 输入资源组名称:输入要在其中创建 Spring 应用实例的资源组的名称。 如果要使用默认名称,请按 Enter
  • Sku:选择要用于 Spring 应用实例的 SKU。 如果要使用默认数字,请按 Enter
  • 输入应用名称(演示):提供应用名称。 如果要使用默认项目项目 ID,请按 Enter
  • 运行时:选择要用于 Spring 应用实例的运行时。 在这种情况下,应使用默认数字,请按 Enter
  • 公开此应用的公共访问权限 (boot-for-azure):按 y
  • 确认保存上述所有配置:按 y。 如果按 n,则配置不会保存在 .pom 文件中。

以下示例显示了部署过程的输出:

Summary of properties:
Subscription id   : 12345678-1234-1234-1234-123456789101
Resource group name : rg-ms-identity-spring-boot-webapp
Azure Spring Apps name : cluster-ms-identity-spring-boot-webapp
Runtime Java version : Java 11
Region            : eastus
Sku               : Standard
App name          : ms-identity-spring-boot-webapp
Public access     : true
Instance count/max replicas : 1
CPU count         : 1
Memory size(GB)   : 2
Confirm to save all the above configurations (Y/n):
[INFO] Configurations are saved to: /home/user/ms-identity-java-spring-tutorial/1-Authentication/sign-in/pom.    xml
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:57 min
[INFO] Finished at: 2024-02-14T13:50:44Z
[INFO] ------------------------------------------------------------------------

确认选择后,该插件会将所需的插件元素和设置添加到项目的 pom.xml 文件中,以将应用配置为在 Azure Spring Apps 中运行。

pom.xml文件的相关部分应类似于以下示例:

<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-spring-apps-maven-plugin</artifactId>
    <version>1.19.0</version>
    <configuration>
        <subscriptionId>12345678-1234-1234-1234-123456789101</subscriptionId>
        <resourceGroup>rg-ms-identity-spring-boot-webapp</resourceGroup>
        <clusterName>cluster-ms-identity-spring-boot-webapp</clusterName>
        <region>eastus</region>
        <sku>Standard</sku>
        <appName>ms-identity-spring-boot-webapp</appName>
        <isPublic>true</isPublic>
        <deployment>
            <cpu>1</cpu>
            <memoryInGB>2</memoryInGB>
            <instanceCount>1</instanceCount>
            <runtimeVersion>Java 11</runtimeVersion>
            <resources>
                <resource>
                    <directory>${project.basedir}/target</directory>
                    <includes>
                        <include>*.jar</include>
                    </includes>
                </resource>
            </resources>
        </deployment>
    </configuration>
</plugin>

可以直接在 pom.xml 文件中修改 Azure Spring Apps 的配置。 下表列出了一些常见配置:

properties 必选 说明
subscriptionId false 订阅的 ID。
resourceGroup Azure Spring Apps 实例的 Azure 资源组。
clusterName Azure Spring Apps 群集名称。 如果使用已部署 Azure Spring Apps 实例的订阅和资源组,也可以使用此现有群集部署到该群集。
appName Azure Spring Apps 中的应用的名称。
region false 要在其中托管 Azure Spring Apps 实例的区域。 默认值为 eastus。 有关有效区域,请参阅 支持的区域
sku false Azure Spring Apps 实例的定价层。 默认值 Basic仅适用于开发和测试环境。
runtime false 运行时环境配置。 有关详细信息,请参阅配置详细信息
deployment false 部署配置。 有关详细信息,请参阅配置详细信息

有关配置的完整列表,请参阅插件参考文档。 所有 Azure Maven 插件共享一组常见的配置。 有关这些配置,请参阅 常见配置。 有关特定于 Azure Spring Apps 的配置,请参阅 Azure Spring Apps:配置详细信息

请务必保存保留这些 clusterName 值, appName 供以后使用。

准备应用进行部署

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

  1. 导航到应用的 src\main\resources\application.yml 文件并更改已部署应用的域名的值 post-logout-redirect-uri ,如以下示例所示。 例如,如果在cluster-ms-identity-spring-boot-webapp上一步中选择了 Azure Spring Apps 实例和ms-identity-spring-boot-webapp应用名称,则现在必须用于https://cluster-ms-identity-spring-boot-webapp-ms-identity-spring-boot-webapp.azuremicroservices.iopost-logout-redirect-uri该值。

    post-logout-redirect-uri: https://<cluster-name>-<app-name>.azuremicroservices.io
    
  2. 保存此文件后,使用以下命令重新生成应用:

    mvn clean package
    

重要

应用程序的application.yml文件当前在参数中client-secret保存客户端机密的值。 最好将此值保存在此文件中。 如果将其提交到 Git 存储库,则也可能面临风险。

作为额外的安全步骤,可以将此值存储在 Azure 密钥库中,并从密钥库加载机密,使其在应用程序中可用。

更新 Microsoft Entra ID 应用注册

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

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

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

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

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

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

  6. 填写应用的 URI,追加 /login/oauth2/code/ - 例如。 https://<cluster-name>-<app-name>.azuremicroservices.io/login/oauth2/code/

  7. 选择“保存”。

部署应用

使用以下命令部署应用:

mvn azure-spring-apps:deploy

以下列表描述了命令交互:

  • OAuth2 登录名:需要根据 OAuth2 协议授权登录到 Azure。

执行命令后,你会从以下日志消息中看到部署已成功:

[INFO] Deployment(default) is successfully created
[INFO] Starting Spring App after deploying artifacts...
[INFO] Deployment Status: Running
[INFO]   InstanceName:demo-default-x-xxxxxxxxxx-xxxxx  Status:Running Reason:null       DiscoverStatus:UNREGISTERED
[INFO]   InstanceName:demo-default-x-xxxxxxxxx-xxxxx  Status:Terminating Reason:null       DiscoverStatus:UNREGISTERED
[INFO] Getting public url of app(demo)...
[INFO] Application url: https://<your-Azure-Spring-Apps-instance-name>-demo.azuremicroservices.io

验证应用

部署完成后,使用输出应用程序 URL 来访问应用程序。 按以下步骤检查应用程序的日志,以调查任何部署问题:

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

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

探索示例

使用以下步骤浏览示例:

  1. 请注意屏幕中心显示的已登录或注销状态。
  2. 选择角落中的上下文敏感按钮。 首次运行应用时,此按钮将 读取登录 。 或者,选择 令牌详细信息。 由于此页面受保护且需要身份验证,因此会自动重定向到登录页。
  3. 在下一页上,按照说明使用 Microsoft Entra ID 租户中的帐户登录。
  4. 在同意屏幕上,请注意请求的范围。
  5. 成功完成登录流后,应重定向到主页(显示 登录状态 )或 令牌详细信息 页,具体取决于哪个按钮触发了登录流。
  6. 请注意,上下文相关的按钮现在显示 “注销 ”并显示用户名。
  7. 如果位于主页上,请选择“ID 令牌详细信息以查看一些 ID 令牌解码的声明。
  8. 使用角落中的按钮注销。状态页反映新状态。

关于代码

此示例演示如何使用 适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库将用户登录到 Microsoft Entra ID 租户。 此示例还使用 Spring Oauth2 客户端和 Spring Web 启动器。 此示例使用从 Microsoft Entra ID 获取的 ID 令牌中的声明来显示已登录用户的详细信息。

目录

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

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

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();
}

对于登录,应用向适用于 Java 的 Microsoft Entra ID Spring Boot Starter 客户端库自动配置的 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.authenticatedapplication.yml文件中的属性来配置这些路由。 若要配置应用的特定要求,请将 AadWebApplicationHttpSecurityConfigurer#aadWebApplication 该方法应用于 HttpSecurity 实例。 有关示例,请参阅此应用的 SecurityConfig 类,如以下代码所示:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig  {
    
    @Value("${app.protect.authenticated}")
    private String[] allowedOrigins;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
            .and()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(allowedOrigins).authenticated()
                .anyRequest().permitAll()
                );
        // @formatter:on
        return http.build();
    }

    @Bean
    @RequestScope
    public ServletUriComponentsBuilder urlBuilder() {
        return ServletUriComponentsBuilder.fromCurrentRequest();
    }    
}

详细信息

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