У меня проблемы с использованием расширения saml для обеспечения безопасности в моей программе весенней загрузки. Мы используем ping federate и настроили приложение saml. Он был настроен на локальный хост: 8443 / saml / SSO url и еще один для тестирования внутри моего aws кластера. Это выглядело примерно так: domain.com/saml/SSO.
My aws - это служба приложений ECS, в которой используется балансировщик нагрузки, использующий только порт 443.
Когда я запускал это локально, перенаправление работало отлично, и никаких проблем не возникало. Я использовал это как эталонную реализацию https://github.com/vdenotaris/spring-boot-security-saml-sample/blob/master/src/main/java/com/vdenotaris/spring/boot/security/saml/web/config/WebSecurityConfig.java
Затем, когда я развернул его на нашем aws экземпляре, он не сработал бы внезапно. Казалось, получить бесконечное количество перенаправлений успеха. Когда я смотрел на SAML Trace, мне кажется, что AssertionConsumerServiceURL использует ip-адрес и порт ECS Services вместо балансировщиков нагрузки.
Затем я обнаружил, что существует компонент конфигурации для обработки поддержки балансировщика нагрузки. https://docs.spring.io/spring-security-saml/docs/current/reference/html/configuration-advanced.html
Я попытался реализовать это, и, похоже, ничего не изменилось. AssertionConsumerServiceUrl все еще использовал IP-адрес и выполнял бесконечное перенаправление при успешном завершении.
Я не уверен, правильно ли я настраиваю компонент балансировки нагрузки или есть что-то еще, что я мог бы сделать, чтобы выяснить это.
Вот пример WebSecurityConfig. java
package com.test.config;
import com.test.core.SAMLUserDetailsServiceImpl;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.velocity.app.VelocityEngine;
import org.camunda.bpm.webapp.impl.security.auth.ContainerBasedAuthenticationFilter;
import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.parse.StaticBasicParserPool;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.saml.*;
import org.springframework.security.saml.context.SAMLContextProvider;
import org.springframework.security.saml.context.SAMLContextProviderImpl;
import org.springframework.security.saml.context.SAMLContextProviderLB;
import org.springframework.security.saml.key.JKSKeyManager;
import org.springframework.security.saml.key.KeyManager;
import org.springframework.security.saml.log.SAMLDefaultLogger;
import org.springframework.security.saml.metadata.*;
import org.springframework.security.saml.parser.ParserPoolHolder;
import org.springframework.security.saml.processor.*;
import org.springframework.security.saml.util.VelocityFactory;
import org.springframework.security.saml.websso.*;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.*;
@EnableWebSecurity
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 15)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean, DisposableBean {
private Timer backgroundTaskTimer;
private MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager;
public void init() {
this.backgroundTaskTimer = new Timer(true);
this.multiThreadedHttpConnectionManager = new MultiThreadedHttpConnectionManager();
}
public void shutdown() {
this.backgroundTaskTimer.purge();
this.backgroundTaskTimer.cancel();
this.multiThreadedHttpConnectionManager.shutdown();
}
@Value("${security.saml2.metadata-url}")
String metadataUrl;
@Value("${security.saml2.entity-id}")
String entityId;
@Value("${server.ssl.key-store}")
String keyStore;
@Value("${server.ssl.key-store-password}")
String keyStorePassword;
@Value("${server.ssl.key-alias}")
String keyAlias;
@Value("${server.ssl.key-password}")
String keyPassword;
@Autowired
private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;
// Initialization of the velocity engine
@Bean
public VelocityEngine velocityEngine() {
return VelocityFactory.getEngine();
}
// XML parser pool needed for OpenSAML parsing
@Bean(initMethod = "initialize")
public StaticBasicParserPool parserPool() {
return new StaticBasicParserPool();
}
@Bean(name = "parserPoolHolder")
public ParserPoolHolder parserPoolHolder() {
return new ParserPoolHolder();
}
// Bindings, encoders and decoders used for creating and parsing messages
@Bean
public HttpClient httpClient() {
return new HttpClient(this.multiThreadedHttpConnectionManager);
}
// SAML Authentication Provider responsible for validating of received SAML
// messages
@Bean
public SAMLAuthenticationProvider samlAuthenticationProvider() {
SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
samlAuthenticationProvider.setForcePrincipalAsString(false);
return samlAuthenticationProvider;
}
// Provider of default SAML Context
@Bean
public SAMLContextProviderImpl contextProvider() {
//return new SAMLContextProviderImpl();
SAMLContextProviderLB samlContextProviderLB = new SAMLContextProviderLB();
samlContextProviderLB.setServerName("mydomain.zone"); // SCRUBBED MY LOAD BALANCER
samlContextProviderLB.setScheme("https");
samlContextProviderLB.setServerPort(443);
samlContextProviderLB.setIncludeServerPortInRequestURL(true);
samlContextProviderLB.setContextPath("");
return samlContextProviderLB;
}
// Initialization of OpenSAML library
@Bean
public static SAMLBootstrap sAMLBootstrap() {
return new SAMLBootstrap();
}
// Logger for SAML messages and events
@Bean
public SAMLDefaultLogger samlLogger() {
return new SAMLDefaultLogger();
}
// SAML 2.0 WebSSO Assertion Consumer
@Bean
public WebSSOProfileConsumer webSSOprofileConsumer() {
return new WebSSOProfileConsumerImpl();
}
// SAML 2.0 Holder-of-Key WebSSO Assertion Consumer
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() {
return new WebSSOProfileConsumerHoKImpl();
}
// SAML 2.0 Web SSO profile
@Bean
public WebSSOProfile webSSOprofile() {
return new WebSSOProfileImpl();
}
// SAML 2.0 Holder-of-Key Web SSO profile
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() {
return new WebSSOProfileConsumerHoKImpl();
}
// SAML 2.0 ECP profile
@Bean
public WebSSOProfileECPImpl ecpprofile() {
return new WebSSOProfileECPImpl();
}
@Bean
public SingleLogoutProfile logoutprofile() {
return new SingleLogoutProfileImpl();
}
// Central storage of cryptographic keys
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader
.getResource(this.keyStore);
String storePass = this.keyStorePassword;
Map<String, String> passwords = new HashMap<String, String>();
passwords.put(this.keyAlias, this.keyPassword);
String defaultKey = this.keyAlias;
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
@Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
webSSOProfileOptions.setIncludeScoping(false);
return webSSOProfileOptions;
}
// Entry point to initialize authentication, default values taken from
// properties file
@Bean
public SAMLEntryPoint samlEntryPoint() {
SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
return samlEntryPoint;
}
// Setup advanced info about metadata
@Bean
public ExtendedMetadata extendedMetadata() {
ExtendedMetadata extendedMetadata = new ExtendedMetadata();
extendedMetadata.setIdpDiscoveryEnabled(false);
extendedMetadata.setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
extendedMetadata.setSignMetadata(true);
extendedMetadata.setEcpEnabled(true);
return extendedMetadata;
}
// IDP Discovery Service
@Bean
public SAMLDiscovery samlIDPDiscovery() {
SAMLDiscovery idpDiscovery = new SAMLDiscovery();
idpDiscovery.setIdpSelectionPath("/saml/discovery");
return idpDiscovery;
}
@Bean
@Qualifier("idp-ssocircle")
public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider()
throws MetadataProviderException {
String idpSSOCircleMetadataURL = this.metadataUrl;
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
this.backgroundTaskTimer, httpClient(), idpSSOCircleMetadataURL);
httpMetadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(true);
extendedMetadataDelegate.setMetadataRequireSignature(false);
backgroundTaskTimer.purge();
return extendedMetadataDelegate;
}
// IDP Metadata configuration - paths to metadata of IDPs in circle of trust
// is here
// Do no forget to call iniitalize method on providers
@Bean
@Qualifier("metadata")
public CachingMetadataManager metadata() throws MetadataProviderException {
List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
providers.add(ssoCircleExtendedMetadataProvider());
return new CachingMetadataManager(providers);
}
// Filter automatically generates default SP metadata
@Bean
public MetadataGenerator metadataGenerator() {
MetadataGenerator metadataGenerator = new MetadataGenerator();
metadataGenerator.setEntityId("WMProcessEngineUi-203871");
metadataGenerator.setExtendedMetadata(extendedMetadata());
metadataGenerator.setIncludeDiscoveryExtension(false);
metadataGenerator.setKeyManager(keyManager());
return metadataGenerator;
}
// The filter is waiting for connections on URL suffixed with filterSuffix
// and presents SP metadata there
@Bean
public MetadataDisplayFilter metadataDisplayFilter() {
return new MetadataDisplayFilter();
}
// Handler deciding where to redirect user after successful login
@Bean
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
// Handler deciding where to redirect user after failed login
@Bean
public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() {
SimpleUrlAuthenticationFailureHandler failureHandler =
new SimpleUrlAuthenticationFailureHandler();
failureHandler.setUseForward(true);
failureHandler.setDefaultFailureUrl("/error");
return failureHandler;
}
@Bean
public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
return samlWebSSOHoKProcessingFilter;
}
// Processing filter for WebSSO profile messages
@Bean
public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
return samlWebSSOProcessingFilter;
}
@Bean
public MetadataGeneratorFilter metadataGeneratorFilter() {
return new MetadataGeneratorFilter(metadataGenerator());
}
// Handler for successful logout
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl("/");
return successLogoutHandler;
}
// Logout handler terminating local session
@Bean
public SecurityContextLogoutHandler logoutHandler() {
SecurityContextLogoutHandler logoutHandler =
new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(true);
logoutHandler.setClearAuthentication(true);
return logoutHandler;
}
// Filter processing incoming logout messages
// First argument determines URL user will be redirected to after successful
// global logout
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
return new SAMLLogoutProcessingFilter(successLogoutHandler(),
logoutHandler());
}
// Overrides default logout processing filter with the one processing SAML
// messages
@Bean
public SAMLLogoutFilter samlLogoutFilter() {
return new SAMLLogoutFilter(successLogoutHandler(),
new LogoutHandler[]{logoutHandler()},
new LogoutHandler[]{logoutHandler()});
}
// Bindings
private ArtifactResolutionProfile artifactResolutionProfile() {
final ArtifactResolutionProfileImpl artifactResolutionProfile =
new ArtifactResolutionProfileImpl(httpClient());
artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
return artifactResolutionProfile;
}
@Bean
public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) {
return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile());
}
@Bean
public HTTPSOAP11Binding soapBinding() {
return new HTTPSOAP11Binding(parserPool());
}
@Bean
public HTTPPostBinding httpPostBinding() {
return new HTTPPostBinding(parserPool(), velocityEngine());
}
@Bean
public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() {
return new HTTPRedirectDeflateBinding(parserPool());
}
@Bean
public HTTPSOAP11Binding httpSOAP11Binding() {
return new HTTPSOAP11Binding(parserPool());
}
@Bean
public HTTPPAOS11Binding httpPAOS11Binding() {
return new HTTPPAOS11Binding(parserPool());
}
// Processor
@Bean
public SAMLProcessorImpl processor() {
Collection<SAMLBinding> bindings = new ArrayList<SAMLBinding>();
bindings.add(httpRedirectDeflateBinding());
bindings.add(httpPostBinding());
bindings.add(artifactBinding(parserPool(), velocityEngine()));
bindings.add(httpSOAP11Binding());
bindings.add(httpPAOS11Binding());
return new SAMLProcessorImpl(bindings);
}
/**
* Define the security filter chain in order to support SSO Auth by using SAML 2.0
*
* @return Filter chain proxy
* @throws Exception
*/
@Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"),
samlIDPDiscovery()));
return new FilterChainProxy(chains);
}
/**
* Returns the authentication manager currently used by Spring.
* It represents a bean definition with the aim allow wiring from
* other classes performing the Inversion of Control (IoC).
*
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* Defines the web based security configuration.
*
* @param http It allows configuring web based security for specific http requests.
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(samlFilter(), CsrfFilter.class);
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/saml/**").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/js/**").permitAll()
.anyRequest().authenticated();
http
.logout()
.disable(); // The logout procedure is already handled by SAML filters.
}
/**
* Sets a custom authentication provider.
*
* @param auth SecurityBuilder used to create an AuthenticationManager.
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(samlAuthenticationProvider());
}
@Override
public void afterPropertiesSet() throws Exception {
init();
}
@Override
public void destroy() throws Exception {
shutdown();
}
@Bean
public FilterRegistrationBean containerBasedAuthenticationFilter() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new ContainerBasedAuthenticationFilter());
filterRegistration.setInitParameters(Collections.singletonMap("authentication-provider", "com.example.webapp.SpringSecurityAuthenticationProvider")); // SCRUBBED
filterRegistration.setOrder(101); // make sure the filter is registered after the Spring Security Filter Chain
filterRegistration.addUrlPatterns("/app/*");
return filterRegistration;
}
}
Вот как выглядит журнал при бесконечном l oop.
INFO: Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-03-22 15:21:44.590 INFO 8 --- [-nio-443-exec-7] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-03-22 15:21:44.600 INFO 8 --- [-nio-443-exec-7] o.s.web.servlet.DispatcherServlet : Completed initialization in 10 ms
2020-03-22 15:21:44.614 INFO 8 --- [-nio-443-exec-1] o.s.s.s.m.MetadataGeneratorFilter : No default metadata configured, generating with default values, please pre-configure metadata for production use
2020-03-22 15:21:44.615 WARN 8 --- [-nio-443-exec-1] o.s.s.s.m.MetadataGeneratorFilter : Generated default entity base URL https://WW.WWW.WWW.WWW:32827 based on values in the first server request. Please set property entityBaseURL on MetadataGenerator bean to fixate the value.
2020-03-22 15:21:44.767 INFO 8 --- [-nio-443-exec-1] o.s.s.s.m.MetadataGeneratorFilter : Created default metadata for system with entityID: WMProcessEngineUi-203871
2020-03-22 15:21:45.113 INFO 8 --- [-nio-443-exec-1] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;XX.XXX.XXX.XX;WMProcessEngineUi-203871;https://ssobeta.domain.com;;;
2020-03-22 15:21:45.113 INFO 8 --- [-nio-443-exec-7] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;YY.YYY.YYY.YYY;WMProcessEngineUi-203871;https://ssobeta.domain.com;;;
2020-03-22 15:21:45.113 INFO 8 --- [-nio-443-exec-9] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;ZZ.ZZZ.ZZZ.ZZZ;WMProcessEngineUi-203871;https://ssobeta.domain.com;;;
2020-03-22 15:21:59.611 INFO 8 --- [-nio-443-exec-3] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;ZZ.ZZZ.ZZZ.ZZZ;WMProcessEngineUi-203871;https://ssobeta.domain.com;;;
2020-03-22 15:21:59.661 INFO 8 --- [-nio-443-exec-5] o.s.security.saml.log.SAMLDefaultLogger : AuthNRequest;SUCCESS;YY.YYY.YYY.YYY;WMProcessEngineUi-203871;https://ssobeta.domain.com;;;