Azure AD SAML2 SSO对接问题:Spring Boot+AWS ELB协议端口异常
解决Spring Security SAML2在AWS ELB下回复URL协议/端口异常问题
针对部署在ECS的Spring Boot应用,通过SSL终止的ELB访问时,SAML认证请求中回复URL自动变为http和8443的问题,提供以下解决思路:
1. 开启Spring Boot的反向代理转发头支持
ELB作为反向代理会传递X-Forwarded-Proto(前端协议)、X-Forwarded-Port(前端端口)等头信息,Spring Boot需要正确识别这些头来生成外部真实URL。在application.yml中添加配置:
server: forward-headers-strategy: framework tomcat: remoteip: remote-ip-header: X-Forwarded-For protocol-header: X-Forwarded-Proto port-header: X-Forwarded-Port
或application.properties格式:
server.forward-headers-strategy=framework server.tomcat.remoteip.remote-ip-header=X-Forwarded-For server.tomcat.remoteip.protocol-header=X-Forwarded-Proto server.tomcat.remoteip.port-header=X-Forwarded-Port
该配置让Spring Boot使用框架原生机制处理转发头,确保内部请求能获取到外部的HTTPS协议和正确端口。
2. 手动指定SAML2的ACS(断言消费者服务)URL
避免框架自动推断URL时出错,直接在RelyingPartyRegistration中强制指定ACS的完整HTTPS地址:
@Bean public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { RelyingPartyRegistration azureAdRegistration = RelyingPartyRegistrations .fromMetadataLocation("classpath:azure-ad-saml-metadata.xml") .registrationId("azure-ad-sso") .acsLocation("https://our-domain.com/saml/sso") // 明确指定外部ACS URL .build(); return new InMemoryRelyingPartyRegistrationRepository(azureAdRegistration); }
配置后,Spring Security生成SAML认证请求时会直接使用指定的URL,不再依赖请求上下文推断。
3. 验证ELB的转发头配置
确保ELB的HTTPS监听器已开启转发必要头信息:
- 登录AWS控制台进入目标ELB的监听器配置页
- 检查HTTPS监听器的"转发头"设置,确认
X-Forwarded-Proto、X-Forwarded-Port已启用传递
4. 自定义Filter修正请求上下文(兜底方案)
若上述配置未解决问题,可自定义Filter手动覆盖请求的scheme和端口:
@Component public class ForwardedUrlCorrectionFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String protoHeader = request.getHeader("X-Forwarded-Proto"); String portHeader = request.getHeader("X-Forwarded-Port"); if (protoHeader != null && portHeader != null) { HttpServletRequest wrappedRequest = new HttpServletRequestWrapper(request) { @Override public String getScheme() { return protoHeader; } @Override public int getServerPort() { return Integer.parseInt(portHeader); } @Override public StringBuffer getRequestURL() { StringBuffer originalUrl = super.getRequestURL(); String originalScheme = super.getScheme(); int originalPort = super.getServerPort(); return new StringBuffer(originalUrl.toString() .replace(originalScheme + "://", protoHeader + "://") .replace(":" + originalPort, ":" + portHeader)); } }; filterChain.doFilter(wrappedRequest, response); } else { filterChain.doFilter(request, response); } } }
然后在Spring Security配置中,将该Filter添加到SAML2相关Filter之前:
@Configuration @EnableWebSecurity public class SecurityConfig { private final ForwardedUrlCorrectionFilter forwardedUrlFilter; public SecurityConfig(ForwardedUrlCorrectionFilter forwardedUrlFilter) { this.forwardedUrlFilter = forwardedUrlFilter; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .addFilterBefore(forwardedUrlFilter, Saml2WebSsoAuthenticationFilter.class) .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .saml2Login(saml2 -> saml2 .relyingPartyRegistrationRepository(relyingPartyRegistrationRepository())); return http.build(); } // 此处省略RelyingPartyRegistrationRepository的配置 }
补充说明
旧版Spring Security SAML扩展中的SAMLContextProviderLB已在新版Spring Security SAML2中移除,新版已整合反向代理处理机制,上述方案均基于新版标准实现。
内容的提问来源于stack exchange,提问作者Ravi Choudhari
相关产品推荐
相关产品推荐

