适用于 Microsoft Entra 的 Spring Boot 启动器开发人员指南

本文适用于:✅ 版本 4.19.0 ✅ 版本 5.21.0

本文介绍适用于 Microsoft Entra ID 的 Spring Boot Starter 的功能和核心场景。 本文还包括有关常见问题、解决方法和诊断步骤的指导。

构建 Web 应用程序时,标识和访问管理是基础部分。 Azure 提供基于云的标识服务,该服务与 Azure 生态系统的其余部分进行了深度集成。

尽管 Spring Security 可以轻松保护基于 Spring 的应用程序,但它并非针对特定标识提供者定制。 使用 Microsoft Entra ID 的 Spring Boot Starter 可将 Web 应用程序连接到 Microsoft Entra 租户,并使用 Microsoft Entra ID 保护资源服务器。 它使用 Oauth 2.0 协议来保护 Web 应用程序和资源服务器。

以下链接提供对初学者包、文档和示例的访问权限:

先决条件

若要按照本指南中的说明进行作,必须满足以下先决条件:

重要

要完成本文中的步骤,需要 Spring Boot 版本 2.5 或更高版本。

核心场景

本指南介绍如何在以下场景中使用的 Microsoft Entra 入门工具:

Web 应用程序是任何基于 Web 的应用程序,允许用户登录。 验证访问令牌后 ,资源服务器 将接受或拒绝访问。

访问 Web 应用程序

此方案使用 OAuth 2.0 授权代码授予 流,使用户能够使用 Microsoft 帐户登录。

若要在此场景中使用 Microsoft Entra 入门版,请按照以下步骤操作:

将重定向 URI 设置为 <application-base-uri>/login/oauth2/code/. 例如: http://localhost:8080/login/oauth2/code/。 请务必包含尾随的 /。 有关重定向 URI 的详细信息,请参阅快速入门中的 “添加重定向 URI :向 Microsoft标识平台注册应用程序

Azure 门户的屏幕截图,其中突出显示了重定向 URI 的 Web 应用身份验证页。

将以下依赖项添加到 pom.xml 文件。

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

注释

有关如何使用材料清单(BOM)管理 Spring Cloud Azure 库版本的详细信息,请参阅 Spring Cloud Azure 开发人员指南 入门部分。

将以下属性添加到 application.yml 文件。 可以从在 Azure 门户中创建的应用注册获取这些属性的值,如先决条件中所述。

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>

注释

允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。 有关这些值的详细信息,请参阅错误 AADSTS50020 - 来自标识提供者的用户帐户不存在于租户中部分中的使用了错误的终结点(个人和组织帐户)部分。 有关转换单租户应用的信息,请参阅 在 Microsoft Entra ID上将单租户应用转换为多租户。

使用默认安全配置或提供自己的配置。

选项 1:使用默认配置。

使用此选项时,您不需要做任何操作。 DefaultAadWebSecurityConfiguration 类将自动配置。

选项 2:提供自定义配置。

若要提供配置,请对 HttpSecurity 应用 AadWebApplicationHttpSecurityConfigurer#aadWebApplication 方法,如以下示例所示:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
           // Do some custom configuration.
       return http.build();
   }
}

从 Web 应用程序访问资源服务器

若要在此方案中使用 Microsoft Entra 启动程序,请按照以下步骤进行操作:

如前所述设置重定向 URI。

将以下依赖项添加到 pom.xml 文件。

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

注释

有关如何使用材料清单(BOM)管理 Spring Cloud Azure 库版本的详细信息,请参阅 Spring Cloud Azure 开发人员指南 入门部分。

将以下属性添加到 application.yml 文件,如前所述:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>
       authorization-clients:
         graph:
           scopes: https://graph.microsoft.com/Analytics.Read, email

注释

允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。 有关这些值的详细信息,请参阅使用错误的终结点(个人和组织帐户)部分中的错误 AADSTS50020 - 来自身份提供者的用户帐户在租户中不存在。 有关转换单租户应用的信息,请参阅 在 Microsoft Entra ID上将单租户应用转换为多租户。

此处,graphOAuth2AuthorizedClient 的名称,scopes 是登录时同意所需的范围。

将代码添加到应用程序,类似于以下示例:

@GetMapping("/graph")
@ResponseBody
public String graph(
   @RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graphClient
) {
   // toJsonString() is just a demo.
   // oAuth2AuthorizedClient contains access_token. We can use this access_token to access the resource server.
   return toJsonString(graphClient);
}

graph下面是在上一步中配置的客户端 ID。 OAuth2AuthorizedClient 包含用于访问资源服务器的访问令牌。

有关演示此方案的完整示例,请参阅 spring-cloud-azure-starter-active-directory 示例:aad-web-application

保护资源服务器/API

此方案不支持登录,但通过验证访问令牌来保护服务器。 如果访问令牌有效,服务器会提供请求。

若要在这种情况下使用 Microsoft Entra 启动器,请使用以下步骤:

将以下依赖项添加到 pom.xml 文件。

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

注释

有关如何使用材料清单(BOM)管理 Spring Cloud Azure 库版本的详细信息,请参阅 Spring Cloud Azure 开发人员指南 入门部分。

将以下属性添加到 application.yml 文件,如前所述:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       credential:
         client-id: <your-client-ID>
       app-id-uri: <your-app-ID-URI>

可以同时使用<your-client-ID><your-app-ID-URI>的值来验证访问令牌。 可以从 Azure 门户获取 <your-app-ID-URI> 该值,如下图所示:

显示 Web 应用的 Azure 门户的屏幕截图:“公开 API”页,突出显示了应用程序 ID URI。

使用默认安全配置或提供自己的配置。

选项 1:使用默认配置。

使用此选项时,无需执行任何操作。 DefaultAadResourceServerConfiguration 类是自动配置的。

选项 2:提供自定义配置。

若要提供配置,请对 HttpSecurity 应用 AadResourceServerHttpSecurityConfigurer#aadResourceServer方法,如以下示例所示:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2ResourceServerSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
       http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       return http.build();
   }
}

有关演示此方案的完整示例,请参阅 spring-cloud-azure-starter-active-directory 示例:aad-resource-server

从资源服务器访问其他资源服务器

此方案支持资源服务器访问其他资源服务器。

要在此场景中使用 Microsoft Entra 入门程序,请按以下步骤操作:

将以下依赖项添加到 pom.xml 文件。

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

注释

有关如何使用材料清单(BOM)管理 Spring Cloud Azure 库版本的详细信息,请参阅 Spring Cloud Azure 开发人员指南 入门部分。

将以下属性添加到 application.yml 文件

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <web-API-A-client-ID>
         client-secret: <web-API-A-client-secret>
       app-id-uri: <web-API-A-app-ID-URI>
       authorization-clients:
         graph:
           scopes:
              - https://graph.microsoft.com/User.Read

注释

允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。 有关这些值的详细信息,请参阅错误 AADSTS50020 - 来自标识提供者的用户帐户不存在于租户中部分中的使用了错误的终结点(个人和组织帐户)部分。 有关转换单租户应用的信息,请参阅 在 Microsoft Entra ID上将单租户应用转换为多租户。

@RegisteredOAuth2AuthorizedClient使用代码中的属性访问相关资源服务器,如以下示例所示:

@PreAuthorize("hasAuthority('SCOPE_Obo.Graph.Read')")
@GetMapping("call-graph")
public String callGraph(@RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graph) {
   return callMicrosoftGraphMeEndpoint(graph);
}

有关展示该场景的完整示例,请参阅 spring-cloud-azure-starter-active-directory 示例:aad-resource-server-obo

一个应用程序中的 Web 应用程序和资源服务器

此方案支持 访问 Web 应用程序 并在一个应用程序中 保护资源服务器/API

若要在此方案中使用 aad-starter ,请执行以下步骤:

将以下依赖项添加到 pom.xml 文件。

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

注释

有关如何使用材料清单(BOM)管理 Spring Cloud Azure 库版本的详细信息,请参阅 Spring Cloud Azure 开发人员指南 入门部分。

更新 application.yml 文件。 将属性 spring.cloud.azure.active-directory.application-type 设置为 web_application_and_resource_server,并为每个授权客户端指定授权类型,如以下示例所示。

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <Web-API-C-client-id>
         client-secret: <Web-API-C-client-secret>
       app-id-uri: <Web-API-C-app-id-url>
       application-type: web_application_and_resource_server  # This is required.
       authorization-clients:
         graph:
           authorizationGrantType: authorization_code  # This is required.
           scopes:
             - https://graph.microsoft.com/User.Read
             - https://graph.microsoft.com/Directory.Read.All

注释

允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。 有关这些数值的更多信息,请参阅使用错误的终结点(个人和组织帐户)一节中的错误AADSTS50020 - 标识提供者的用户帐户在租户中不存在。 有关转换单租户应用的信息,请参阅 在 Microsoft Entra ID上将单租户应用转换为多租户。

编写 Java 代码以配置多个 HttpSecurity 实例。

在以下示例代码中, AadWebApplicationAndResourceServerConfig 包含两个安全筛选器链豆,一个用于资源服务器,一个用于 Web 应用程序。 apiFilterChain bean 具有配置资源服务器安全生成器的高优先级。 htmlFilterChain bean 的优先级较低,用于配置 Web 应用程序安全生成器。

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadWebApplicationAndResourceServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
                .and()
            // All the paths that match `/api/**`(configurable) work as the resource server. Other paths work as the web application.
            .securityMatcher("/api/**")
            .authorizeHttpRequests()
                .anyRequest().authenticated();
        return http.build();
    }

    @Bean
    public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                .and()
            .authorizeHttpRequests()
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated();
        // @formatter:on
        return http.build();
    }
}

应用程序类型

属性 spring.cloud.azure.active-directory.application-type 是可选的,因为它的值可以通过依赖项推断。 只有在使用 web_application_and_resource_server 该值时才必须手动设置属性。

具有依赖项:spring-security-oauth2-client 具有依赖项:spring-security-oauth2-resource-server 应用程序类型的有效值 默认值
是的 web_application web_application
是的 resource_server resource_server
是的 是的 web_application,resource_server,
resource_server_with_oboweb_application_and_resource_server
resource_server_with_obo

可配置属性

Microsoft Entra ID 的 Spring Boot Starter 提供以下属性:

性能 说明
spring.cloud.azure.active-directory.app-id-uri 供资源服务器用来验证访问令牌中的受众。 仅当访问群体等于 <your-client-ID> 前面所述的或 <your-app-ID-URI> 值时,访问令牌才有效。
spring.cloud.azure.active-directory.authorization-clients 一个映射,用于配置应用程序将访问的资源 API。 每个项对应于应用程序要访问的一个资源 API。 在 Spring 代码中,每个项对应于一个 OAuth2AuthorizedClient 对象。
spring.cloud.azure.active-directory.authorization-clients.<your-client-name>.scopes 应用程序要获取的资源服务器的 API 权限。
spring.cloud.azure.active-directory.authorization-clients.<your-client-name>.authorization-grant-type 授权客户端的类型。 支持的类型为authorization_code(webapp 的默认类型), on_behalf_of (资源服务器的默认类型)。 client_credentials
spring.cloud.azure.active-directory.application-type 请参阅 应用程序类型
spring.cloud.azure.active-directory.profile.environment.active-directory-endpoint 授权服务器的基本 URI。 默认值是 https://login.microsoftonline.com/
spring.cloud.azure.active-directory.credential.client-id Microsoft Entra ID 中注册的应用程序 ID。
spring.cloud.azure.active-directory.credential.client-secret 已注册应用程序的客户端密码。
spring.cloud.azure.active-directory.user-group.use-transitive-members 如果设置为 true,则使用 v1.0/me/transitiveMemberOf 来获取组。 否则使用 /v1.0/me/memberOf
spring.cloud.azure.active-directory.post-logout-redirect-uri 用于发布注销的重定向 URI。
spring.cloud.azure.active-directory.profile.tenant-id Azure 租户 ID。 允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。
spring.cloud.azure.active-directory.user-group.allowed-group-names 如果在 MemberOf Graph API 调用的响应中找到,将被授予权限的预期用户组。
spring.cloud.azure.active-directory.user-name-attribute 指示哪个声明将成为主体的名称。

以下示例演示如何使用这些属性:

属性示例 1: 若要使用 Azure 中国世纪互联 而不是 Azure Global,请使用以下步骤。

  • 将以下属性添加到 application.yml 文件

    spring:
       cloud:
         azure:
           active-directory:
             enabled: true
             profile:
               environment:
                 active-directory-endpoint: https://login.partner.microsoftonline.cn
    

使用此方法,可以使用 Azure 主权云或国家云 ,而不是 Azure 公有云。

属性示例 2: 若要使用组名称保护 Web 应用程序中的某些方法,请执行以下步骤:

将以下属性添加到 application.yml 文件:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       user-group:
         allowed-groups: group1, group2

使用默认安全配置或提供自己的配置。

选项 1:使用默认配置。 使用此选项时,无需执行任何操作。 DefaultAadWebSecurityConfiguration 类被自动配置。

选项 2:提供自定义配置。 若要提供配置,请为 HttpSecurity 应用 AadWebApplicationHttpSecurityConfigurer#aadWebApplication 方法 ,如以下示例所示:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
       // @formatter:off
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       // @formatter:on
       // Do some custom configuration.
       return http.build();
   }
}

使用@PreAuthorize批注来保护方法,如以下示例所示:

@Controller
public class RoleController {
   @GetMapping("group1")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group1')")
   public String group1() {
       return "group1 message";
   }

   @GetMapping("group2")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group2')")
   public String group2() {
       return "group2 message";
   }

   @GetMapping("group1Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group1-id>')")
   public String group1Id() {
       return "group1Id message";
   }

   @GetMapping("group2Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group2-id>')")
   public String group2Id() {
       return "group2Id message";
   }
}

属性示例 3: 若要在访问资源服务器的资源服务器中启用客户端凭据流,请使用以下步骤:

将以下属性添加到 application.yml 文件:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       authorization-clients:
         webapiC:   # When authorization-grant-type is null, on behalf of flow is used by default
           authorization-grant-type: client_credentials
           scopes:
             - <Web-API-C-app-id-url>/.default

将代码添加到应用程序,类似于以下示例:

@PreAuthorize("hasAuthority('SCOPE_Obo.WebApiA.ExampleScope')")
@GetMapping("webapiA/webapiC")
public String callClientCredential() {
   String body = webClient
       .get()
       .uri(CUSTOM_LOCAL_READ_ENDPOINT)
       .attributes(clientRegistrationId("webapiC"))
       .retrieve()
       .bodyToMono(String.class)
       .block();
   LOGGER.info("Response from Client Credential: {}", body);
   return "client Credential response " + (null != body ? "success." : "failed.");
}

高级功能

在 Web 应用程序中支持按 ID 令牌进行访问控制

启动器支持从 ID 令牌的 roles 声明中创建 GrantedAuthority,以便在 Web 应用程序中使用 ID 令牌进行授权。 可以使用 appRoles Microsoft Entra ID 的功能来创建 roles 声明并实现访问控制。

注释

appRoles生成的roles声明被用前缀APPROLE_修饰。

使用appRoles作为roles声明时,请避免同时将组属性配置为roles。 否则,组属性将替代声明来包含组信息而不是 appRoles。 应避免在清单中进行以下配置:

"optionalClaims": {
    "idtoken": [{
        "name": "groups",
        "additionalProperties": ["emit_as_roles"]
    }]
}

若要在 Web 应用程序中支持按 ID 令牌进行访问控制,请执行以下步骤:

在应用程序中添加应用角色,并将其分配给用户或组。 有关详细信息,请参阅 “如何:向应用程序添加应用角色并在令牌中接收它们”。

将以下 appRoles 配置添加到应用程序的清单:

 "appRoles": [
   {
     "allowedMemberTypes": [
       "User"
     ],
     "displayName": "Admin",
     "id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
     "isEnabled": true,
     "description": "Full admin access",
     "value": "Admin"
    }
 ]

将代码添加到应用程序,类似于以下示例:

@GetMapping("Admin")
@ResponseBody
@PreAuthorize("hasAuthority('APPROLE_Admin')")
public String Admin() {
   return "Admin message";
}

故障排除

启用客户端日志记录

适用于 Java 的 Azure SDK 提供了一致的日志记录故事,可帮助排查和解决应用程序错误。 生成的日志将在到达终端之前捕获应用程序的流,帮助找到根问题。 查看 日志记录 Wiki,获取有关启用日志记录的指导。

启用 Spring 日志记录

Spring 允许所有支持的日志记录系统在 Spring 环境中设置记录器级别,例如在 application.properties 中,通过使用 logging.level.<logger-name>=<level>,其中 <level>TRACEDEBUGINFOWARNERRORFATALOFF 之一。 可以使用logging.level.root来配置根记录器。

以下示例显示了 application.properties 文件中的潜在日志记录设置:

logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR

有关 Spring 中的日志记录配置的详细信息,请参阅 Spring 文档中的 日志记录

后续步骤

若要了解有关 Spring 和 Azure 的详细信息,请继续访问“Azure 上的 Spring”文档中心。