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

本文适用于: ✔️ 版本 4.19.0 ✔️ 版本 5.15.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 的详细信息,请查看快速入门:向 Microsoft 标识平台注册应用程序中的添加重定向 URI

显示 Web 应用身份验证页的 Azure 门户屏幕截图,其中突出显示了重定向 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 文件。 可从你在 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:使用默认配置。

使用此选项,你无需执行任何操作。 DefaultAadWebSecurityConfigurerAdapter 类会自动配置。

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

若要提供配置,请扩展 AadWebSecurityConfigurerAdapter 类并在 configure(HttpSecurity http) 函数中调用 super.configure(http),如下例所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AadOAuth2LoginSecurityConfig extends AadWebSecurityConfigurerAdapter {

   /**
    * Add configuration logic as needed.
   */
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       super.configure(http);
       http.authorizeRequests()
           .anyRequest().authenticated();
       // Do some custom configuration.
   }
}

从 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:使用默认配置。

使用此选项,你无需执行任何操作。 DefaultAadResourceServerWebSecurityConfigurerAdapter 类会自动配置。

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

若要提供配置,请扩展 AadResourceServerWebSecurityConfigurerAdapter 类并在 configure(HttpSecurity http) 函数中调用 super.configure(http),如下例所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AadOAuth2ResourceServerSecurityConfig extends AadResourceServerWebSecurityConfigurerAdapter {

   /**
    * Add configuration logic as needed.
    */
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       super.configure(http);
       http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
   }
}

有关演示此方案的完整示例,请参阅 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 应用程序。 高优先级的 ApiWebSecurityConfigurationAdapter 类可配置资源服务器安全适配器。 低优先级的 HtmlWebSecurityConfigurerAdapter 类可配置 Web 应用程序安全适配器。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AadWebApplicationAndResourceServerConfig {

   @Order(1)
   @Configuration
   public static class ApiWebSecurityConfigurationAdapter extends AadResourceServerWebSecurityConfigurerAdapter {
       protected void configure(HttpSecurity http) throws Exception {
           super.configure(http);
           // All the paths that match `/api/**`(configurable) work as the esource server. Other paths work as  the web application.
           http.antMatcher("/api/**")
               .authorizeRequests().anyRequest().authenticated();
       }
   }

   @Configuration
   public static class HtmlWebSecurityConfigurerAdapter extends AadWebSecurityConfigurerAdapter {

       @Override
       protected void configure(HttpSecurity http) throws Exception {
           super.configure(http);
           // @formatter:off
           http.authorizeRequests()
                   .antMatchers("/login").permitAll()
                   .anyRequest().authenticated();
           // @formatter:on
       }
   }
}

应用程序类型

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
No resource_server resource_server
web_application,resource_server,
resource_server_with_obo, web_application_and_resource_server
resource_server_with_obo

可配置属性

适用于 Microsoft Entra ID 的 Spring Boot 启动器提供以下属性:

属性 说明
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(用于 resource-server 的默认类型)、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 全球,请执行以下步骤。

  • 将以下属性添加到 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:使用默认配置。 使用此选项,你无需执行任何操作。 DefaultAadWebSecurityConfigurerAdapter 类会自动配置。

选项 2:提供自定义配置。 若要提供配置,请扩展 AadWebSecurityConfigurerAdapter 类并在 configure(HttpSecurity http) 函数中调用 super.configure(http),如下例所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AadOAuth2LoginSecurityConfig extends AadWebSecurityConfigurerAdapter {

   /**
    * Add configuration logic as needed.
    */
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       super.configure(http);
       http.authorizeRequests()
           .anyRequest().authenticated();
       // Do some custom configuration.
   }
}

使用 @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,来允许使用 ID 令牌在 Web 应用程序中进行授权。 可以使用 Microsoft Entra ID 的 appRoles 功能创建 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,所有支持的日志记录系统可使用 logging.level.<logger-name>=<level> 在 Spring 环境(例如在 application.properties 中)中设置记录器级别,其中级别为“跟踪”、“调试”、“信息”、“警告”、“错误”、“严重”或“关”。 可使用 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”文档中心。