You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多Azure AD环境下Spring应用统一登录页适配多租户问题咨询

实现Spring应用多Azure AD租户统一登录页的方案

我之前帮不少开发者解决过类似的多Azure AD租户统一登录的问题,结合Spring Security的OAuth2支持,其实可以通过以下步骤实现你的需求——让不同企业租户共用你自定义的统一登录页,而不是跳转到各自的Azure AD默认登录页:

1. 先把Azure AD应用设为多租户模式

首先得在Azure门户里把你的应用注册配置成多租户类型(选择"任何Azure AD目录中的用户"),这是基础前提,否则你的应用只能对接单个租户。

2. 配置Spring Security支持多租户OAuth2客户端

接下来要在Spring项目里配置每个租户的Azure AD认证信息,让应用能识别不同租户的授权端点。

步骤1:确保依赖到位

你的Spring Boot项目里必须引入spring-boot-starter-oauth2-client依赖,这是对接Azure AD OIDC认证的核心依赖。

步骤2:配置各租户的认证信息

application.yml(或application.properties)里,为每个租户单独配置客户端和提供者信息,示例如下:

spring:
  security:
    oauth2:
      client:
        registration:
          tenant1:
            client-id: <你的租户1客户端ID>
            client-secret: <你的租户1客户端密钥>
            scope: openid,profile,email
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-name: 企业租户1
          tenant2:
            client-id: <你的租户2客户端ID>
            client-secret: <你的租户2客户端密钥>
            scope: openid,profile,email
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-name: 企业租户2
        provider:
          tenant1:
            authorization-uri: https://login.microsoftonline.com/<租户1ID>/oauth2/v2.0/authorize
            token-uri: https://login.microsoftonline.com/<租户1ID>/oauth2/v2.0/token
            user-info-uri: https://graph.microsoft.com/oidc/userinfo
            jwk-set-uri: https://login.microsoftonline.com/<租户1ID>/discovery/v2.0/keys
            user-name-attribute: name
          tenant2:
            authorization-uri: https://login.microsoftonline.com/<租户2ID>/oauth2/v2.0/authorize
            token-uri: https://login.microsoftonline.com/<租户2ID>/oauth2/v2.0/token
            user-info-uri: https://graph.microsoft.com/oidc/userinfo
            jwk-set-uri: https://login.microsoftonline.com/<租户2ID>/discovery/v2.0/keys
            user-name-attribute: name

这里的registrationId(比如tenant1、tenant2)是每个租户的唯一标识,后面会用到。

3. 自定义统一登录页

Spring Security默认会生成一个简单的登录页,但我们需要自己做一个统一风格的页面,让用户选择对应的租户登录。

步骤1:创建自定义登录页

比如用Thymeleaf创建login.html,放在src/main/resources/templates目录下,页面上放各个租户的登录按钮:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>企业统一登录</title>
</head>
<body>
    <h2>欢迎登录,请选择您的企业租户</h2>
    <form th:action="@{/oauth2/authorization/tenant1}" method="get">
        <button type="submit">登录【企业租户1】</button>
    </form>
    <br>
    <form th:action="@{/oauth2/authorization/tenant2}" method="get">
        <button type="submit">登录【企业租户2】</button>
    </form>
</body>
</html>

步骤2:配置Spring Security使用自定义登录页

写一个SecurityConfig类,告诉Spring Security用我们自己的登录页,同时开启OAuth2客户端支持:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .anyRequest().authenticated() // 所有请求都需要认证
            )
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login") // 指定自定义登录页的路径
                .permitAll() // 允许所有人访问登录页
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            );
        return http.build();
    }
}

这样,当用户访问需要认证的资源时,就会跳转到你自定义的统一登录页,用户选择租户后,会跳转到对应租户的Azure AD登录页完成认证,之后自动回到你的应用。

4. 进阶:让用户输入邮箱自动识别租户(可选)

如果你不想让用户手动选租户,也可以做一个邮箱输入框,通过邮箱域名自动匹配租户,动态跳转到对应授权页面:

步骤1:写一个后端接口处理租户识别

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.RedirectView;

import java.util.Map;

@Controller
public class TenantLoginController {

    // 维护邮箱域名和租户ID的映射关系
    private static final Map<String, String> DOMAIN_TENANT_MAP = Map.of(
        "company1.com", "<租户1ID>",
        "company2.com", "<租户2ID>"
    );

    private static final String CLIENT_ID = "<你的应用全局客户端ID>";
    private static final String REDIRECT_URI = "https://your-app-domain/login/oauth2/code/common";

    @PostMapping("/login-by-email")
    public RedirectView redirectToTenantAuth(@RequestParam String email) {
        try {
            String domain = email.split("@")[1];
            String tenantId = DOMAIN_TENANT_MAP.get(domain);
            if (tenantId == null) {
                return new RedirectView("/login?error=unknown");
            }
            // 构建对应租户的授权URL
            String authUrl = String.format(
                "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=openid profile email",
                tenantId, CLIENT_ID, REDIRECT_URI
            );
            return new RedirectView(authUrl);
        } catch (Exception e) {
            return new RedirectView("/login?error=invalid");
        }
    }
}

步骤2:修改登录页添加邮箱输入框

<form th:action="@{/login-by-email}" method="post">
    <input type="email" name="email" placeholder="请输入您的企业邮箱" required>
    <button type="submit">登录</button>
</form>

5. 关键注意事项

  • 每个租户的Azure AD应用注册里,必须添加你的应用的重定向URI,否则认证后无法回调到应用
  • 客户端密钥要妥善保管,生产环境建议用Azure Key Vault之类的安全存储方案
  • 多租户场景下,不同租户的用户ID格式可能不同,要确保你的应用能正确识别唯一用户

内容的提问来源于stack exchange,提问作者ced

火山引擎 最新活动