Spring Cloud Azure 支持 for Spring Security

本文适用于: ✔️版本 4.14.0 ✔️ 版本 5.8.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 帐户登录用户。

系统关系图

System diagram for a standalone web application.

在 Azure 中创建所需的资源

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

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

  3. 设置为 redirect URIAPPLICATION_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 登录页。

高级用法

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

在 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 Government

  • 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。

System diagram for redirect URIs.

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

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

在Azure 门户中更新redirect-uri

Configure Redirect URI Template.

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

@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.oauth2Login()
                .loginProcessingUrl("${REDIRECT-URI-TEMPLATE}")
                .and()
            .authorizeRequests()
                .anyRequest().authenticated();
    }
}

通过代理连接到 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 应用程序

系统关系图

System diagram for a web application accessing resource servers.

在 Azure 中创建所需的资源

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

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

  3. 设置为 redirect URIAPPLICATION_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 上的“将单租户应用转换为多租户”。

graph此处为名称OAuth2AuthorizedClientscopes表示登录时需要同意的范围。

在应用程序中使用 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

访问资源服务器

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

系统关系图

System diagram for standalone resource server usage.

在 Azure 中创建所需的资源

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

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

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

  4. 公开名为 <a0/a0> 范围的 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 文档。

高级用法

添加额外的安全配置
@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());
    }
}
按范围验证权限
  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 bean。 有关详细信息,请参阅通过代理部分连接到 Microsoft Entra ID。

示例

示例项目: aad-resource-server

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

系统关系图

System diagram for a resource server visiting other resource servers.

在 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 应用程序的两个安全筛选器链配置。

@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 `Resource Server`, other paths work as `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-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
No resource_server resource_server
web_application, resource_server, resource_server_with_obo, web_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. 访问其他资源服务器的资源服务器。

System diagram of web application interaction with Microsoft Entra ID and resource servers.

用法 1:访问 Web 应用程序

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

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

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

从应用程序中选择要生成的WEB_APP_AZURE_CLIENT_SECRET密钥然后选择“保存”。

选择 左侧的用户流 ,然后选择“ 新建用户流”。

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

选择 API 权限“添加 Microsoft API 权限>>”、“Microsoft Graph”、“委派权限”、“offline_access”和“开放权限”,然后选择“添加权限”以完成该过程。

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

Azure portal screenshot showing API permissions screen for an app, with graph permissions highlighted.

将以下依赖项添加到 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-springsecurity5</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";
   }
}

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

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

   private final AadB2cOidcLoginConfigurer configurer;

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

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       // @formatter:off
       http.authorizeRequests()
               .anyRequest().authenticated()
               .and()
           .apply(configurer);
       // @formatter:off
   }
}

从 aad-b2c-web-application 示例复制 home.html,并替换PASSWORD_RESET_USER_FLOWPROFILE_EDIT_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 记录为你 WEB_API_A_APP_ID_URL,然后选择“ 保存”。

从导航窗格中选择 “清单 ”,然后将以下 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"
}

Azure portal screenshot showing application manifest screen with appRoles JSON highlighted.

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

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

Azure portal screenshot showing application API permissions screen.

根据 访问 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 代码,请参阅 “访问资源服务器 ”部分。

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

用法 3:访问资源服务器

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

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

为 Web 应用程序添加 WebApiA 权限并授予管理员同意。

将以下依赖项添加到 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.";
   }
}

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

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests((requests) -> requests.anyRequest().authenticated())
           .oauth2ResourceServer()
           .jwt()
               .jwtAuthenticationConverter(new AadJwtBearerTokenAuthenticationConverter());
   }
}

生成并测试应用。 让我们 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"
}

Azure portal screenshot showing application WebApiB manifest screen with appRoles JSON highlighted.

授予管理员权限 WebApiB 许可。

Azure portal screenshot showing application WebApiA API permissions screen.

在访问资源服务器的基础上,将以下依赖项添加到 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.";
   }
}

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

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

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

示例

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