Spring Cloud Azure 对 Spring Security 的支持

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

本文介绍如何结合使用 Spring Cloud Azure 和 Spring Security。

具有 Microsoft Entra ID 的 Spring Security

构建 Web 应用程序时,标识和访问管理始终是基础部分。

Azure 提供了一个实现应用程序开发旅程民主化的绝佳平台,因为它不仅提供云基础标识服务,而且还与 Azure 生态系统的其余部分深度集成。

Spring Security 使用功能强大的抽象和可扩展接口轻松保护基于 Spring 的应用程序。 但是,与 Spring 框架一样强大,它不是针对特定标识提供者定制的。

spring-cloud-azure-starter-active-directory 提供了将 Web 应用程序连接到 Microsoft Entra ID(Microsoft 用于短租户的 Entra ID)的最佳方法,并使用 Microsoft Entra ID 保护资源服务器。 它使用 Oauth 2.0 协议来保护 Web 应用程序和资源服务器。

访问 Web 应用程序

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

系统关系图

独立 Web 应用程序的系统关系图。

在 Azure 中创建所需的资源

  1. 阅读 快速入门:将应用程序注册到Microsoft标识平台

  2. 创建应用注册。 获取 AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRET

  3. redirect URI 设置为 APPLICATION_BASE_URI/login/oauth2/code/ ,例如 http://localhost:8080/login/oauth2/code/。 尾 / 是必需的。

添加所需的依赖项

<dependencies>
    <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>
</dependencies>

添加所需的属性

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        profile:
          tenant-id: <tenant>
        credential:
          client-id: ${AZURE_CLIENT_ID}
          client-secret: ${AZURE_CLIENT_SECRET}

注意

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

现在,启动应用程序并通过浏览器访问应用程序。 你将重定向到Microsoft登录页。

高级用法

添加额外的安全配置
@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();
   }
}
按应用角色授予访问权限

在 Azure 中创建所需的资源:

注意

如果要使用基于应用角色的访问控制,则无法在 role 声明中放置组名称。 有关详细信息,请参阅 配置组可选声明 部分 向应用提供可选声明

保护特定方法。

class Demo {
   @GetMapping("Admin")
   @ResponseBody
   @PreAuthorize("hasAuthority('APPROLE_Admin')")
   public String admin() {
       return "Admin message";
   }
}
按组名称或组 ID 授予访问权限

添加相关的配置属性。

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       user-group:
         allowed-group-names: group1_name_1, group2_name_2
         # 1. If allowed-group-ids == all, then all group ID will take effect.
         # 2. If "all" is used, we should not configure other group ids.
         # 3. "all" is only supported for allowed-group-ids, not supported for allowed-group-names.
         allowed-group-ids: group_id_1, group_id_2

保护特定方法。

@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";
   }
}
使用国家 Azure 而不是全局 Azure

除了全局 Azure 云之外,Microsoft Entra ID 部署在以下国家/地区云中:

  • Azure 政府版

  • Azure 中国世纪互联

  • Azure 德国

下面是使用 Azure 中国世纪互联的示例。

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        base-uri: https://login.partner.microsoftonline.cn
        graph-base-uri: https://microsoftgraph.chinacloudapi.cn

有关详细信息,请参阅 国家云部署

配置重定向 URI 模板

开发人员可以自定义重定向 URI。

重定向 URI 的系统关系图。

redirect-uri-template 文件中添加 属性。

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       redirect-uri-template: ${REDIRECT-URI-TEMPLATE}

在 Azure 门户中更新 redirect-uri

配置重定向 URI 模板。

设置 redirect-uri-template后,需要更新安全生成器:

@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()
            .oauth2Login()
                .loginProcessingUrl("${REDIRECT-URI-TEMPLATE}")
                .and()
            .authorizeHttpRequests()
                .anyRequest().authenticated();
        // @formatter:on
        return http.build();
    }
}

通过代理连接到 Microsoft Entra ID

若要通过代理连接 Microsoft Entra ID,请提供 RestTemplateCustomizer 豆类,如以下示例所示:

@Configuration
class DemoConfiguration {
    @Bean
    public RestTemplateCustomizer proxyRestTemplateCustomizer() {
        return (RestTemplate restTemplate) -> {
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_SERVER_HOST, PROXY_SERVER_PORT));
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            requestFactory.setProxy(proxy);
            restTemplate.setRequestFactory(requestFactory);
        };
    }
}

样品

示例项目:aad-web-application

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

系统关系图

访问资源服务器的 Web 应用程序的系统关系图。

在 Azure 中创建所需的资源

  1. 阅读 快速入门:将应用程序注册到Microsoft标识平台

  2. 创建应用注册。 获取 AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRET

  3. redirect URI 设置为 APPLICATION_BASE_URI/login/oauth2/code/,例如 http://localhost:8080/login/oauth2/code/。 尾 / 是必需的。

添加所需的依赖项

<dependencies>
    <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>
</dependencies>

添加所需的属性

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

注意

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

此处,graphOAuth2AuthorizedClient的名称,scopes 表示登录时需要同意的范围。

在应用程序中使用 OAuth2AuthorizedClient

public class Demo {
    @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 resource server.
        return toJsonString(graphClient);
    }
}

现在,启动应用程序并在浏览器中访问应用程序。 然后,你将重定向到Microsoft登录页。

高级用法

客户端凭据流

如果想要 客户端凭据流授权代码流,则可以如下所示进行配置:

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        profile:
          tenant-id: <tenant>
        credential:
          client-id: ${AZURE_CLIENT_ID}
          client-secret: ${AZURE_CLIENT_SECRET}
        authorization-clients:
          graph:
            authorization-grant-type: client_credentials # Change type to client_credentials
            scopes: https://graph.microsoft.com/Analytics.Read, email

注意

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

访问多个资源服务器

在一个 Web 应用程序中,可以通过配置如下所示来访问多个资源服务器:

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        profile:
          tenant-id: <tenant>
        credential:
          client-id: ${AZURE_CLIENT_ID}
          client-secret: ${AZURE_CLIENT_SECRET}
        authorization-clients:
          resource-server-1:
            scopes: # Scopes for resource-server-1
          resource-server-2:
            scopes: # Scopes for resource-server-2

注意

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

然后,可以在如下所示的应用程序中使用 OAuth2AuthorizedClient

public class Demo {
    @GetMapping("/resource-server-1")
    @ResponseBody
    public String graph(
    @RegisteredOAuth2AuthorizedClient("resource-server-1") OAuth2AuthorizedClient client) {
        return callResourceServer1(client);
    }

    @GetMapping("/resource-server-2")
    @ResponseBody
    public String graph(
    @RegisteredOAuth2AuthorizedClient("resource-server-2") OAuth2AuthorizedClient client) {
        return callResourceServer2(client);
    }
}

样品

示例项目:aad-web-application

访问资源服务器

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

系统关系图

独立资源服务器使用情况的系统关系图。

在 Azure 中创建所需的资源

  1. 阅读 快速入门:将应用程序注册到Microsoft标识平台

  2. 创建应用注册。 获取 AZURE_CLIENT_ID

  3. 阅读 快速入门:配置应用程序以公开 Web API

  4. 公开名为 Scope-1的范围的 Web API。

添加所需的依赖项

<dependencies>
    <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>
</dependencies>

添加所需的属性

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        credential:
          client-id: ${AZURE_CLIENT_ID}

现在启动应用程序并访问应用程序的 Web API。

  1. 你将获得 401,而无需访问令牌。

  2. 使用访问令牌访问应用程序。 将验证访问令牌中的以下声明:

    • iss:访问令牌必须由 Microsoft Entra ID 颁发。

    • nbf:当前时间不能在 nbf之前。

    • exp:当前时间不能在 exp之后。

    • aud:如果已配置 spring.cloud.azure.active-directory.credential.client-idspring.cloud.azure.active-directory.credential.app-id-uri,则受众必须等于配置的 client-idapp-id-uri。 如果未配置这两个属性,则不会验证此声明。

有关访问令牌的详细信息,请参阅有关Microsoft标识平台访问令牌MS 文档。

高级用法

添加额外的安全配置
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2ResourceServerSecurityConfig {

    /**
     * Add configuration logic as needed.
     */
    @Bean
    public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
            .and()
            .authorizeHttpRequests()
            .anyRequest().authenticated();
        // @formatter:on
        return http.build();
    }
}
按范围验证权限
  1. 在 Azure 中创建所需的资源。

  2. 保护特定方法。

    class Demo {
        @GetMapping("scope1")
        @ResponseBody
        @PreAuthorize("hasAuthority('SCOPE_Scope1')")
        public String scope1() {
            return "Congratulations, you can access `scope1` endpoint.";
        }
    }
    

通过执行此操作,访问 /scope1 终结点时,将验证访问令牌中的以下声明:

  • scp:该值必须包含 Scope1
按应用角色验证权限
  1. 在 Azure 中创建所需的资源。

  2. 保护特定方法。

    class Demo {
        @GetMapping("app-role1")
        @ResponseBody
        @PreAuthorize("hasAuthority('APPROLE_AppRole1')")
        public String appRole1() {
            return "Congratulations, you can access `app-role1` endpoint.";
        }
    }
    

通过执行此操作,访问 /app-role1 终结点时,将验证访问令牌中的以下声明:

  • roles:该值必须包含 AppRole1
使用 JWT 客户端身份验证

若要使用 JSON Web 令牌(JWT)进行客户端身份验证,请执行以下步骤:

  1. 请参阅 Microsoft标识平台 部分 Microsoft标识平台应用程序身份验证证书凭据
  2. .pem 证书上传到在 Azure 门户中注册的应用程序。
  3. 配置 的证书路径和密码。PFX。P12 证书。
  4. 将属性 spring.cloud.azure.active-directory.authorization-clients.azure.client-authentication-method=private_key_jwt 配置添加到客户端,以便通过 JWT 客户端身份验证进行身份验证。

以下示例配置文件适用于 Web 应用程序方案。 证书信息在全局属性中配置。

spring:
  cloud:
    azure:
      credential:
        client-id: ${AZURE_CLIENT_ID}
        client-certificate-path: ${AZURE_CERTIFICATE_PATH}
        client-certificate-password: ${AZURE_CERTIFICATE_PASSWORD}
      profile:
        tenant-id: <tenant>
      active-directory:
        enabled: true
        user-group:
          allowed-group-names: group1,group2
          allowed-group-ids: <group1-id>,<group2-id>
        post-logout-redirect-uri: http://localhost:8080
        authorization-clients:
          azure:
            client-authentication-method: private_key_jwt
          arm:
            client-authentication-method: private_key_jwt
            scopes: https://management.core.windows.net/user_impersonation
          graph:
            client-authentication-method: private_key_jwt
            scopes:
              - https://graph.microsoft.com/User.Read
              - https://graph.microsoft.com/Directory.Read.All
          webapiA:
            client-authentication-method: private_key_jwt
            scopes:
              - ${WEB_API_A_APP_ID_URL}/Obo.WebApiA.ExampleScope
          webapiB:
            client-authentication-method: private_key_jwt
            scopes:
              - ${WEB_API_B_APP_ID_URL}/.default
            authorization-grant-type: client_credentials

注意

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

还可以在 active-directory 服务属性中配置证书信息,如以下示例所示:

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        credential:
          client-id: ${AZURE_CLIENT_ID}
          client-certificate-path: ${AZURE_CERTIFICATE_PATH}
          client-certificate-password: ${AZURE_CERTIFICATE_PASSWORD}
        profile:
          tenant-id: <tenant>
        user-group:
          allowed-group-names: group1,group2
          allowed-group-ids: <group1-id>,<group2-id>
        post-logout-redirect-uri: http://localhost:8080
        authorization-clients:
          azure:
            client-authentication-method: private_key_jwt
          arm:
            client-authentication-method: private_key_jwt
            scopes: https://management.core.windows.net/user_impersonation
          graph:
            client-authentication-method: private_key_jwt
            scopes:
              - https://graph.microsoft.com/User.Read
              - https://graph.microsoft.com/Directory.Read.All
          webapiA:
            client-authentication-method: private_key_jwt
            scopes:
              - ${WEB_API_A_APP_ID_URL}/Obo.WebApiA.ExampleScope
          webapiB:
            client-authentication-method: private_key_jwt
            scopes:
              - ${WEB_API_B_APP_ID_URL}/.default
            authorization-grant-type: client_credentials

注意

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

通过代理连接到 Microsoft Entra ID

若要通过代理连接 Microsoft Entra ID,请提供 RestTemplateCustomizer 豆。 有关详细信息,请参阅 通过代理 部分连接到 Microsoft Entra ID。

样品

示例项目:aad-resource-server

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

系统关系图

访问其他资源服务器的资源服务器的系统关系图。

在 Azure 中创建所需的资源

  1. 阅读 快速入门:将应用程序注册到Microsoft标识平台

  2. 创建应用注册。 获取 AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRET

添加所需的依赖项

<dependencies>
    <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>
</dependencies>

添加所需的属性

spring:
  cloud:
    azure:
      active-directory:
        enabled: true
        profile:
          tenant-id: <tenant>
        credential:
          client-id: ${AZURE_CLIENT_ID}
          client-secret: ${AZURE_CLIENT_SECRET}
        authorization-clients:
          graph:
            scopes:
              - https://graph.microsoft.com/User.Read

注意

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

在应用程序中使用 OAuth2AuthorizedClient

public class SampleController {
    @GetMapping("call-graph")
    public String callGraph(@RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graph) {
        return callMicrosoftGraphMeEndpoint(graph);
    }
}

样品

示例项目:aad-resource-server-obo

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

在 Azure 中创建所需的资源

  1. 阅读 快速入门:将应用程序注册到Microsoft标识平台

  2. 创建应用注册。 获取 AZURE_TENANT_IDAZURE_CLIENT_IDAZURE_CLIENT_SECRET

添加所需的依赖项

<dependencies>
    <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>
</dependencies>

添加所需的属性

将属性 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: ${AZURE_CLIENT_ID}
          client-secret: ${AZURE_CLIENT_SECRET}
        app-id-uri: ${WEB_API_ID_URI}
        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上将单租户应用转换为多租户。

定义 SecurityFilterChain

配置多个 SecurityFilterChain 实例。 AadWebApplicationAndResourceServerConfig 包含资源服务器和 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-starter-active-directory 的可配置属性:

名字 描述
spring.cloud.azure.active-directory.app-id-uri 可在id_token的“aud”声明中使用的应用 ID URI。
spring.cloud.azure.active-directory.application-type Microsoft Entra 应用程序的类型。
spring.cloud.azure.active-directory.authenticate-additional-parameters 将其他参数添加到授权 URL。
spring.cloud.azure.active-directory.authorization-clients OAuth2 授权客户端。
spring.cloud.azure.active-directory.credential.client-id 使用 Azure 执行服务主体身份验证时要使用的客户端 ID。
spring.cloud.azure.active-directory.credential.client-secret 使用 Azure 执行服务主体身份验证时要使用的客户端密码。
spring.cloud.azure.active-directory.jwk-set-cache-生命期 缓存的 JWK 在过期之前设置的生命周期,默认值为 5 分钟。
spring.cloud.azure.active-directory.jwk-set-cache-refresh-time 缓存的 JWK 在过期之前设置的刷新时间,默认值为 5 分钟。
spring.cloud.azure.active-directory.jwt-connect-timeout JWKSet 远程 URL 调用的连接超时。
spring.cloud.azure.active-directory.jwt-read-timeout JWKSet 远程 URL 调用的读取超时。
spring.cloud.azure.active-directory.jwt-size-limit JWKSet 远程 URL 调用的大小限制(以字节为单位)。
spring.cloud.azure.active-directory.post-logout-redirect-uri 注销后的重定向 URI。
spring.cloud.azure.active-directory.profile.cloud-type 要连接到的 Azure 云的名称。 支持的类型包括:AZURE、AZURE_CHINA、AZURE_GERMANY、AZURE_US_GOVERNMENT、OTHER。
spring.cloud.azure.active-directory.profile.environment Microsoft Entra 终结点的属性。
spring.cloud.azure.active-directory.profile.tenant-id Azure 租户 ID。 允许 tenant-id 的值包括:commonorganizationsconsumers或租户 ID。
spring.cloud.azure.active-directory.redirect-uri-template 重定向终结点:授权服务器用于通过资源所有者用户代理将包含授权凭据的响应返回到客户端。 默认值为 {baseUrl}/login/oauth2/code/
spring.cloud.azure.active-directory.resource-server.claim-to-authority-prefix-map 配置将用于生成 GrantedAuthority 的声明以及 GrantedAuthority 字符串值的前缀。 默认值为:“scp”->“SCOPE_”、“roles”->“APPROLE_”。
spring.cloud.azure.active-directory.resource-server.principal-claim-name 在 AuthenticatedPrincipal#getName 中配置访问令牌中返回的声明。 默认值为“sub”。
spring.cloud.azure.active-directory.session-stateless 如果 true 激活无状态身份验证筛选器 AadAppRoleStatlessAuthenticationFilter。 默认值为 false,用于激活 AadAuthenticationFilter。
spring.cloud.azure.active-directory.user-group.allowed-group-ids 组 ID 可用于构造 GrantedAuthority。
spring.cloud.azure.active-directory.user-group.allowed-group-names 组名称可用于构造 GrantedAuthority。
spring.cloud.azure.active-directory.user-group.use-transitive-members 如果为“true”,请使用“v1.0/me/transitiveMemberOf”获取成员。 否则,请使用“v1.0/me/memberOf”。 默认值为 false
spring.cloud.azure.active-directory.user-name-attribute 确定要作为主体名称的声明。

下面是有关如何使用这些属性的一些示例:

应用程序类型

可以从依赖项推断应用程序类型:spring-security-oauth2-clientspring-security-oauth2-resource-server。 如果推断的值不是所需的值,则可以指定应用程序类型。 下面是有效值和推断值表:

spring-cloud-azure-starter-active-directory的应用程序类型:

具有依赖项:spring-security-oauth2-client 具有依赖项:spring-security-oauth2-resource-server 应用程序类型的有效值 推断值
是的 web_application web_application
是的 resource_server resource_server
是的 是的 web_applicationresource_serverresource_server_with_oboweb_application_and_resource_server resource_server_with_obo

Azure Active Directory B2C 的 Spring Security

Azure Active Directory (Azure AD) B2C 是一种标识管理服务,可用于自定义和控制客户在使用应用程序时如何注册、登录和管理其配置文件。 Azure AD B2C 支持这些操作,同时保护客户的标识。

依赖项设置

<dependencies>
    <dependency>
        <groupId>com.azure.spring</groupId>
        <artifactId>spring-cloud-azure-starter-active-directory-b2c</artifactId>
    </dependency>
</dependencies>

配置

spring-cloud-azure-starter-active-directory-b2c 的可配置属性:

名字 描述
spring.cloud.azure.active-directory.b2c.app-id-uri 令牌的“aud”声明中使用的应用 ID URI。
spring.cloud.azure.active-directory.b2c.authenticate-additional-parameters 身份验证的其他参数。
spring.cloud.azure.active-directory.b2c.authorization-clients 指定客户端配置。
spring.cloud.azure.active-directory.b2c.base-uri Azure AD B2C 终结点基 URI。
spring.cloud.azure.active-directory.b2c.credential Azure AD B2C 凭据信息。
spring.cloud.azure.active-directory.b2c.jwt-connect-timeout JWKSet 远程 URL 调用的连接超时。
spring.cloud.azure.active-directory.b2c.jwt-read-timeout JWKSet 远程 URL 调用的读取超时。
spring.cloud.azure.active-directory.b2c.jwt-size-limit JWKSet 远程 URL 调用的大小限制(以字节为单位)。
spring.cloud.azure.active-directory.b2c.login-flow 指定主登录流密钥。 默认值为 sign-up-or-sign-in
spring.cloud.azure.active-directory.b2c.logout-success-url 注销后重定向 URL。 默认值为 http://localhost:8080/login
spring.cloud.azure.active-directory.b2c.profile Azure AD B2C 配置文件信息。
spring.cloud.azure.active-directory.b2c.reply-url 获取授权代码后回复 URL。 默认值为 {baseUrl}/login/oauth2/code/
spring.cloud.azure.active-directory.b2c.user-flow 用户流。
spring.cloud.azure.active-directory.b2c.user-name-attribute-name 用户名属性名称。

有关完整配置,请查看 Spring Cloud Azure 配置属性

基本用法

Web 应用程序 是任何基于 Web 的应用程序,它允许用户使用 Microsoft Entra ID 登录,而 资源服务器 在验证从 Microsoft Entra ID 获取access_token后接受或拒绝访问。 本指南将介绍 4 种方案:

  1. 访问 Web 应用程序。

  2. 访问资源服务器的 Web 应用程序。

  3. 访问资源服务器。

  4. 访问其他资源服务器的资源服务器。

用法 1:访问 Web 应用程序

此方案使用 OAuth 2.0 授权代码授予 流,以使用 Azure AD B2C 用户登录用户。

从门户菜单中选择 Azure AD B2C,选择 应用程序,然后选择 添加

指定应用程序 名称(例如 webapp),为 http://localhost:8080/login/oauth2/code/添加 ,将 应用程序 ID 记录为 WEB_APP_AZURE_CLIENT_ID,然后选择 “保存”。

从应用程序中选择 密钥,选择 生成密钥 以生成 WEB_APP_AZURE_CLIENT_SECRET,然后选择 保存

选择左侧 用户流,然后选择 新用户流

选择 注册或配置文件编辑密码重置 分别创建用户流。 指定用户流 名称用户属性和声明,然后选择 创建

选择 API 权限>添加权限>Microsoft API,选择 Microsoft Graph,选择 委派的权限,选择 offline_accessopenid 权限,然后选择 添加权限 以完成该过程。

授予 Graph 权限的管理员同意。

Azure 门户屏幕截图,其中显示了应用的 API 权限屏幕,其中突出显示了图形权限。

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

<dependencies>
   <dependency>
       <groupId>com.azure.spring</groupId>
       <artifactId>azure-spring-boot-starter-active-directory-b2c</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-thymeleaf</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>
   <dependency>
       <groupId>org.thymeleaf.extras</groupId>
       <artifactId>thymeleaf-extras-springsecurity6</artifactId>
   </dependency>
</dependencies>

使用前面创建的值将属性添加到 application.yml 文件,如以下示例所示:

spring:
 cloud:
   azure:
     active-directory:
       b2c:
         enabled: true
         authenticate-additional-parameters:
           domain_hint: xxxxxxxxx         # optional
           login_hint: xxxxxxxxx          # optional
           prompt: [login,none,consent]   # optional
         base-uri: ${BASE_URI}
         credential:
           client-id: ${WEBAPP_AZURE_CLIENT_ID}
           client-secret: ${WEBAPP_AZURE_CLIENT_SECRET}
         login-flow: ${LOGIN_USER_FLOW_KEY}               # default to sign-up-or-sign-in, will look up the user-flows map with provided key.
         logout-success-url: ${LOGOUT_SUCCESS_URL}
         user-flows:
           ${YOUR_USER_FLOW_KEY}: ${USER_FLOW_NAME}
         user-name-attribute-name: ${USER_NAME_ATTRIBUTE_NAME}

编写 Java 代码。

对于控制器代码,可以参考以下示例:

@Controller
public class WebController {

   private void initializeModel(Model model, OAuth2AuthenticationToken token) {
       if (token != null) {
           final OAuth2User user = token.getPrincipal();
           model.addAllAttributes(user.getAttributes());
           model.addAttribute("grant_type", user.getAuthorities());
           model.addAttribute("name", user.getName());
       }
   }

   @GetMapping(value = { "/", "/home" })
   public String index(Model model, OAuth2AuthenticationToken token) {
       initializeModel(model, token);
       return "home";
   }
}

对于安全配置代码,可以参考以下示例:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class WebSecurityConfiguration {

    private final AadB2cOidcLoginConfigurer configurer;

    public WebSecurityConfiguration(AadB2cOidcLoginConfigurer configurer) {
        this.configurer = configurer;
    }

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.authorizeHttpRequests()
                .anyRequest().authenticated()
                .and()
            .apply(configurer);
        // @formatter:on
        return http.build();
    }
}

aad-b2c-web-application 示例复制 home.html,并将 PROFILE_EDIT_USER_FLOWPASSWORD_RESET_USER_FLOW 替换为之前使用的用户流名称。

生成并测试应用。 让 Webapp 在端口 8080上运行。

在 Maven 生成并启动应用程序后,在 Web 浏览器中打开 http://localhost:8080/。 应重定向到登录页。

选择登录用户流的链接。 应重定向 Azure AD B2C 以启动身份验证过程。

成功登录后,应会看到来自浏览器的示例 home page

使用情况 2:访问资源服务器的 Web 应用程序

此方案基于 访问 Web 应用程序 方案,以允许应用程序访问其他资源。 此方案 OAuth 2.0 客户端凭据授予 流。

从门户菜单中选择 Azure AD B2C,选择 应用程序,然后选择 添加

指定应用程序 名称(如 webApiA),将 应用程序 ID 记录为 WEB_API_A_AZURE_CLIENT_ID,然后选择 保存

从应用程序中选择 密钥,选择 生成密钥 以生成 WEB_API_A_AZURE_CLIENT_SECRET,然后选择 保存

选择 从导航窗格中公开 API,然后选择 设置。 将 应用程序 ID URI 记录为 ,然后选择“保存

从导航窗格中选择 清单,然后将以下 JSON 段粘贴到 appRoles 数组中。 将 应用程序 ID URI 记录为 WEB_API_A_APP_ID_URL,将应用角色的值记录为 WEB_API_A_ROLE_VALUE,然后选择 “保存”。

{
 "allowedMemberTypes": [
   "Application"
 ],
 "description": "WebApiA.SampleScope",
 "displayName": "WebApiA.SampleScope",
 "id": "04989db0-3efe-4db6-b716-ae378517d2b7",
 "isEnabled": true,
 "value": "WebApiA.SampleScope"
}

选择 API 权限>添加权限>我的 API,选择 WebApiA 应用程序名称,选择 应用程序权限,选择 WebApiA.SampleScope 权限,然后选择 添加权限 以完成该过程。

授予 WebApiA 权限的管理员许可。

Azure 门户屏幕截图,其中显示了应用程序 API 权限屏幕。

访问 Web 应用程序 方案的基础上添加以下依赖项。

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

访问 Web 应用程序 方案的基础上添加以下配置。

spring:
 cloud:
   azure:
     active-directory:
       b2c:
         enabled: true
         base-uri: ${BASE_URI}             # Such as: https://xxxxb2c.b2clogin.com
         profile:
           tenant-id: <tenant>
         authorization-clients:
           ${RESOURCE_SERVER_A_NAME}:
             authorization-grant-type: client_credentials
             scopes: ${WEB_API_A_APP_ID_URL}/.default

注意

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

编写 Webapp Java 代码。

对于控制器代码,可以参考以下示例:

class Demo {
   /**
    * Access to protected data from Webapp to WebApiA through client credential flow. The access token is obtained by webclient, or
    * <p>@RegisteredOAuth2AuthorizedClient("webApiA")</p>. In the end, these two approaches will be executed to
    * DefaultOAuth2AuthorizedClientManager#authorize method, get the access token.
    *
    * @return Respond to protected data from WebApi A.
    */
   @GetMapping("/webapp/webApiA")
   public String callWebApiA() {
       String body = webClient
           .get()
           .uri(LOCAL_WEB_API_A_SAMPLE_ENDPOINT)
           .attributes(clientRegistrationId("webApiA"))
           .retrieve()
           .bodyToMono(String.class)
           .block();
       LOGGER.info("Call callWebApiA(), request '/webApiA/sample' returned: {}", body);
       return "Request '/webApiA/sample'(WebApi A) returned a " + (body != null ? "success." : "failure.");
   }
}

安全配置代码与访问 web 应用程序 方案中 相同。 添加另一种豆 webClient,如下所示:

public class SampleConfiguration {
   @Bean
   public WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
       ServletOAuth2AuthorizedClientExchangeFilterFunction function =
           new ServletOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager);
       return WebClient.builder()
                       .apply(function.oauth2Configuration())
                       .build();
   }
}

若要编写 WebApiA Java 代码,请参阅 访问资源服务器 部分。

生成并测试应用。 让 WebappWebApiA 分别在端口 80808081 上运行。 启动 WebappWebApiA 应用程序。 成功登录后返回到主页。 然后,可以访问 http://localhost:8080/webapp/webApiA 来获取 WebApiA 资源响应。

用法 3:访问资源服务器

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

若要生成 WebApiA 权限,请参阅 使用情况 2:Web 应用程序访问资源服务器

添加 WebApiA 权限并授予 Web 应用程序的管理员许可。

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

<dependencies>
   <dependency>
       <groupId>com.azure.spring</groupId>
       <artifactId>azure-spring-boot-starter-active-directory-b2c</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
</dependencies>

添加以下配置。

spring:
 cloud:
   azure:
     active-directory:
       b2c:
         enabled: true
         base-uri: ${BASE_URI}             # Such as: https://xxxxb2c.b2clogin.com
         profile:
           tenant-id: <tenant>
         app-id-uri: ${APP_ID_URI}         # If you're using v1.0 token, configure app-id-uri for `aud` verification
         credential:
           client-id: ${AZURE_CLIENT_ID}           # If you're using v2.0 token, configure client-id for `aud` verification
         user-flows:
           sign-up-or-sign-in: ${SIGN_UP_OR_SIGN_IN_USER_FLOW_NAME}

注意

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

编写 Java 代码。

对于控制器代码,可以参考以下示例:

class Demo {
   /**
    * webApiA resource api for web app
    * @return test content
    */
   @PreAuthorize("hasAuthority('APPROLE_WebApiA.SampleScope')")
   @GetMapping("/webApiA/sample")
   public String webApiASample() {
       LOGGER.info("Call webApiASample()");
       return "Request '/webApiA/sample'(WebApi A) returned successfully.";
   }
}

对于安全配置代码,可以参考以下示例:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class ResourceServerConfiguration {
    
    @Bean
    public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
        JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix("APPROLE_");
        authenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        // @formatter:off
        http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(authenticationConverter);
        // @formatter:on
        return http.build();
    }
}

生成并测试应用。 让 WebApiA 在端口 8081上运行。 获取 webApiA 资源的访问令牌,然后以持有者授权标头的形式访问 http://localhost:8081/webApiA/sample

使用情况 4:访问其他资源服务器的资源服务器

此方案是 访问资源服务器升级,并支持基于 OAuth2 客户端凭据流访问其他应用程序资源。

在前面的步骤中,我们将创建一个 WebApiB 应用程序并公开应用程序权限 WebApiB.SampleScope

{
   "allowedMemberTypes": [
       "Application"
   ],
   "description": "WebApiB.SampleScope",
   "displayName": "WebApiB.SampleScope",
   "id": "04989db0-3efe-4db6-b716-ae378517d2b7",
   "isEnabled": true,
   "lang": null,
   "origin": "Application",
   "value": "WebApiB.SampleScope"
}

授予 WebApiB 权限的管理员许可。

Azure 门户屏幕截图,其中显示了应用程序 WebApiA API 权限屏幕。

访问资源服务器的基础上,将以下依赖项添加到 pom.xml 文件中。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

访问资源服务器 方案配置的基础上添加以下配置。

spring:
 cloud:
   azure:
     active-directory:
       b2c:
         enabled: true
         credential:
           client-secret: ${WEB_API_A_AZURE_CLIENT_SECRET}
         authorization-clients:
           ${RESOURCE_SERVER_B_NAME}:
             authorization-grant-type: client_credentials
             scopes: ${WEB_API_B_APP_ID_URL}/.default

编写 Java 代码。

对于 WebApiA 控制器代码,可以参考以下示例:

public class SampleController {
   /**
    * Access to protected data from WebApiA to WebApiB through client credential flow. The access token is obtained by webclient, or
    * <p>@RegisteredOAuth2AuthorizedClient("webApiA")</p>. In the end, these two approaches will be executed to
    * DefaultOAuth2AuthorizedClientManager#authorize method, get the access token.
    *
    * @return Respond to protected data from WebApi B.
    */
   @GetMapping("/webApiA/webApiB/sample")
   @PreAuthorize("hasAuthority('APPROLE_WebApiA.SampleScope')")
   public String callWebApiB() {
       String body = webClient
           .get()
           .uri(LOCAL_WEB_API_B_SAMPLE_ENDPOINT)
           .attributes(clientRegistrationId("webApiB"))
           .retrieve()
           .bodyToMono(String.class)
           .block();
       LOGGER.info("Call callWebApiB(), request '/webApiB/sample' returned: {}", body);
       return "Request 'webApiA/webApiB/sample'(WebApi A) returned a " + (body != null ? "success." : "failure.");
   }
}

对于 WebApiB 控制器代码,可以参考以下示例:

public class SampleController {
   /**
    * webApiB resource api for other web application
    * @return test content
    */
   @PreAuthorize("hasAuthority('APPROLE_WebApiB.SampleScope')")
   @GetMapping("/webApiB/sample")
   public String webApiBSample() {
       LOGGER.info("Call webApiBSample()");
       return "Request '/webApiB/sample'(WebApi B) returned successfully.";
   }
}

安全配置代码与 访问资源服务器 方案相同,添加另一个豆类 webClient,如下所示

public class SampleConfiguration {
   @Bean
   public WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
       ServletOAuth2AuthorizedClientExchangeFilterFunction function =
           new ServletOAuth2AuthorizedClientExchangeFilterFunction(oAuth2AuthorizedClientManager);
       return WebClient.builder()
                       .apply(function.oauth2Configuration())
                       .build();
   }
}

生成并测试应用。 让 WebApiAWebApiB 分别在端口 80818082 上运行。 启动 WebApiAWebApiB 应用程序,获取 webApiA 资源的访问令牌,并作为持有者授权标头访问 http://localhost:8081/webApiA/webApiB/sample

样品

有关详细信息,请参阅 spring-cloud-azure-starter-active-directory-b2c 示例