多Azure AD环境下Spring应用统一登录页适配多租户问题咨询
我之前帮不少开发者解决过类似的多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




