1. 简介

本文将演示如何轻松地在Spring Boot应用中使用AzureAD作为身份认证提供者。我们将展示完整的集成步骤,包括配置、客户端注册和权限映射等关键环节。

2. 概述

AzureAD是微软推出的综合性身份管理产品,被全球众多组织广泛采用。它支持多种登录机制和控制策略,能为组织内的应用组合提供单点登录体验。

特别值得注意的是,AzureAD与现有Active Directory安装深度集成——这正是许多组织在企业网络中用于身份和访问管理的基础设施。管理员可以使用熟悉的工具为现有用户分配应用权限,统一管理访问控制。

3. 集成AzureAD

从Spring Boot应用的角度看,AzureAD表现为一个符合OIDC标准的身份提供者。这意味着我们只需配置必要的属性和依赖,就能与Spring Security无缝集成。

我们将实现一个保密客户端,其中授权码与访问令牌的交换在服务端完成。这种流程永远不会将访问令牌暴露给用户浏览器,因此比公共客户端方案更安全

4. Maven依赖

首先添加基于Spring Security的WebMVC应用所需依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
    <version>3.1.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.1.5</version>
</dependency>

最新版本可在Maven中央仓库获取:

5. 配置属性

接下来添加配置Spring Security所需的属性。最佳实践是将这些属性放在独立的Spring Profile中,便于应用扩展时维护。我们创建名为azuread的profile,在application-azuread.yml中配置:

spring:
  security:
    oauth2:
      client:
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/your-tenant-id-comes-here/v2.0
        registration:
          azure-dev:
            provider: azure
            #client-id: 外部提供
            #client-secret: 外部提供         
            scope:
            - openid
            - email
            - profile

在provider部分定义azure提供者:AzureAD支持OIDC标准端点发现机制,因此只需配置issuer-uri属性

该属性有双重作用:

  1. 作为基础URI,客户端追加发现资源名后获取实际下载URL
  2. 用于验证JSON Web Token (JWT)的真实性(JWT的iss声明必须与此值匹配)

AzureAD的issuer-uri格式固定为:https://login.microsoftonline.com/my-tenant-id/v2.0,其中my-tenant-id是你的租户ID。

在registration部分定义使用前述提供者的azure-dev客户端。必须通过client-idclient-secret提供客户端凭据(稍后注册应用时获取)。

scope属性定义授权请求中包含的权限范围。这里请求profile权限,允许客户端访问标准的userinfo端点,该端点返回AzureAD用户目录中的可配置信息集(如用户偏好语言、区域设置等)。

6. 客户端注册

我们需要在AzureAD中注册客户端应用,以获取client-idclient-secret的实际值。假设已有Azure账户,步骤如下:

  1. 登录Web控制台,通过左上角菜单选择Azure Active Directory服务: AD Home

  2. Overview部分获取issuer-uri配置所需的租户标识符

  3. 点击App RegistrationsNew Registration,填写注册表单:

    • 应用名称
    • 支持的账户类型
    • 重定向URI

6.1. 应用名称

此值将在认证过程中展示给最终用户,应选择对目标用户有意义的名称。这里使用示例名称"Baeldung Test App": New App Name

AzureAD允许随时修改名称而不影响已注册应用。虽然名称不必唯一,但多个应用使用相同显示名称并非明智之举。

6.2. 支持的账户类型

根据目标受众选择:组织内部应用通常选择第一项("仅限此组织目录中的账户")。这意味着即使应用可从互联网访问,也只有组织内用户可以登录: New App Account Type

其他选项允许接受其他AzureAD目录(如使用Office 365的学校/组织)以及个人账户(Skype/Xbox)。虽然不常见,但此设置后续可修改,不过文档警告修改后用户可能遇到错误。

6.3. 重定向URI

需提供一个或多个授权流程可接受的重定向URI。需选择与URI关联的"平台"类型:

  • Web:授权码与访问令牌交换在后端进行
  • SPA:交换在前端进行
  • Public Client:用于桌面/移动应用

我们选择Web平台。URI值设为http://localhost:8080/login/oauth2/code/azure-dev,该路径来自Spring Security的OAuth回调控制器默认路径/login/oauth2/code/{registration-name},其中{registration-name}需匹配配置中的注册名称(此处为azure-dev)。

重要提示:AzureAD要求这些URI使用HTTPS,但对localhost有例外,便于本地开发无需证书。部署到生产环境(如Kubernetes集群)时可添加其他URI。

6.4. 添加客户端密钥

完成初始注册后,将显示客户端信息页: App Overview

左侧Essentials部分的应用程序ID对应配置文件中的client-id。生成客户端密钥步骤:

  1. 点击Add a certificate or secretClient Secrets标签 → New client secret
  2. 填写描述性名称和过期时间(预定义期限或自定义起止日期): App Add Secret

⚠️ 当前客户端密钥最长有效期2年,必须建立密钥轮换机制(建议使用Terraform等自动化工具)。企业环境中应用长期运行很常见,两年看似很长实则不然。

点击Add后,新密钥出现在凭据列表: App Credentials List

必须立即复制密钥值到安全位置,离开页面后将不再显示。我们将值直接复制到应用的client-secret属性中。

⚠️ 这是敏感值!生产环境通常通过动态机制(如Kubernetes密钥)提供。

7. 应用代码

测试应用包含一个控制器,处理根路径请求,记录认证信息并转发到Thymeleaf视图,渲染用户信息页面:

@Controller
@RequestMapping("/")
public class IndexController {

    @GetMapping
    public String index(Model model, Authentication user) {
        model.addAttribute("user", user);
        return "index";
    }
}

视图代码使用user模型属性创建认证对象和所有可用声明的信息表格。

8. 运行测试应用

由于使用了AzureAD专用profile,需激活该profile。通过Spring Boot Maven插件运行时,使用spring-boot.run.profiles属性:

mvn -Dspring-boot.run.profiles=azuread spring-boot:run

访问http://localhost:8080,Spring Security检测到未认证请求,重定向到AzureAD通用登录页: Sign in

具体登录流程因组织设置而异,通常包含用户名/邮箱和密码输入,可能要求二次认证。若已在同一浏览器的同一AzureAD租户登录其他应用,将跳过登录流程——这正是单点登录的体现。

首次访问时,AzureAD会显示应用授权表单: Sign in to your account

✅ AzureAD支持自定义登录UI(包括本地化定制),也可完全跳过授权表单(适用于内部应用授权)。

授权后显示应用主页(部分截图): User Info Page

已获取用户基本信息(姓名、邮箱、头像URL等),但用户名显示不友好。接下来优化此问题。

9. 用户名映射

Spring Security使用Authentication接口表示已认证主体,其getName()方法返回的值常作为用户唯一标识。使用JWT认证时,Spring Security默认使用标准sub声明值作为主体名称,但AzureAD在此字段填充内部标识符,不适合显示。

解决方案:在提供者配置中指定user-name-attribute属性,选择可用属性:

spring:
  security:
    oauth2:
      client:
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/xxxxxxxxxxxxx/v2.0
            user-name-attribute: name
... 其他属性省略

这里选择name声明(用户全名),也可选email属性(若应用需用于数据库查询)。重启应用后效果: User Info Page

用户体验显著改善!

10. 获取组成员信息

检查可用声明发现缺少用户组成员信息Authentication中仅包含与请求范围关联的GrantedAuthority值。

若仅需限制组织成员访问,这已足够。但通常需要基于用户角色授予不同访问级别,将角色映射到AzureAD组可复用现有流程(如用户入职/调岗)。

需配置AzureAD在授权流程返回的idToken中包含组成员信息

  1. 进入应用页面 → 右侧菜单Token ConfigurationAdd groups claim
  2. 在对话框中配置声明类型(选择"Security Groups"): Group mapping

保存后应用声明列表显示groups声明: Group mapping

重启应用查看效果: User Info Page

11. 将组映射为Spring权限

groups声明包含用户所属组的对象ID列表,但Spring不会自动将其映射为GrantedAuthority实例。需自定义OidcUserService(参考Spring Security文档)。

我们的实现使用外部映射表为标准OidcUser添加权限。通过@ConfigurationProperties类配置:

  • 获取组列表的声明名称("groups")
  • 权限前缀
  • 对象ID到GrantedAuthority的映射表

组到权限的映射策略允许复用现有组,保持应用角色集与组分配策略解耦。典型配置示例:

baeldung:
  jwt:
    authorization:
      group-to-authorities:
        "ceef656a-fca9-49b6-821b-xxxxxxxxxxxx": BAELDUNG_RW
        "eaaecb69-ccbc-4143-b111-xxxxxxxxxxxx": BAELDUNG_RO,BAELDUNG_ADMIN

对象ID可在Groups页面获取: Group list

完成映射并重启应用后,测试用户(属于两个组)的认证结果: User Info Page

现在包含三个对应映射组的新权限。

12. 总结

本文展示了在Spring Security中使用AzureAD进行用户认证的完整流程,包括演示应用的配置步骤。关键点包括:

  • ✅ 通过OIDC标准实现无缝集成
  • ✅ 保密客户端模式保障令牌安全
  • ✅ 灵活的用户名和权限映射机制
  • ⚠️ 注意客户端密钥的定期轮换

完整代码可在GitHub获取。


原始标题:Authenticating Users with AzureAD in Spring Boot | Baeldung