Я использую следующую среду: springboot 2.1.9.RELEASE spring-security-saml2-core 1.0.10.RELEASE сервер идентификации wso2 5.8.0 Я создал приложение и использовал spring saml2 для его федерации в WSO2.
Я выясняю некоторые проблемы в процессе выхода из системы. Когда я выхожу из системы, я получаю сообщение об ошибке.
Это мой класс WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@PropertySource(value= {"application.properties"})
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean, DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class.getName());
@Autowired
private Environment env;
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();
}
@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;
}
@Bean
public SAMLMessageStorageFactory msgStorage() {
/*
* Come indicato in https://docs.spring.io/spring-security-saml/docs/1.0.x/reference/html/chapter-troubleshooting.html
* Cambiando da http a https (url di wso2 is) e ritornando in http causa la perdita della sessione.
* Disabilitiamo il controllo del campo inResponseTo
*/
//return new HttpSessionStorageFactory();
return new EmptyStorageFactory();
}
// Provider of default SAML Context
@Bean
public SAMLContextProviderImpl contextProvider() {
SAMLContextProviderImpl ctxProvider = new SAMLContextProviderImpl();
return ctxProvider;
}
// 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();
//return new WebSSOProfileImplAttributeConsumingServiceIndex(2);
}
// 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(env.getProperty("intranet.sso.idp.keystore.name"));
String storePass = env.getProperty("intranet.sso.idp.keystore.password");
Map<String, String> passwords = new HashMap<String, String>();
passwords.put(env.getProperty("intranet.sso.idp.keystore.password.key"), env.getProperty("intranet.sso.idp.keystore.password"));
String defaultKey = env.getProperty("intranet.sso.idp.keystore.password.key");
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);*/
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource(env.getProperty("intranet.sso.idp.keystore.name"));
String storePass = env.getProperty("intranet.sso.idp.keystore.password");
Map<String, String> passwords = new HashMap<String, String>();
passwords.put(env.getProperty("intranet.sso.idp.keystore.password.key"), env.getProperty("intranet.sso.idp.keystore.password"));
String defaultKey = env.getProperty("intranet.sso.idp.keystore.password.key");
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
// Setup TLS Socket Factory
@Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
return new TLSProtocolConfigurer();
}
@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();
//Disabilito il discovery. Abbiamo un solo IdP --> WSO2 IdP
extendedMetadata.setIdpDiscoveryEnabled(false);
extendedMetadata.setSignMetadata(false);
extendedMetadata.setEcpEnabled(true);
extendedMetadata.setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
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 Exception {
ClasspathResource idpMetadataResource = new ClasspathResource("/saml/ssocircle-idp-metadata.xml");
ResourceBackedMetadataProvider metadataProvider =
new ResourceBackedMetadataProvider(new Timer(), idpMetadataResource);
metadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(metadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(false);
extendedMetadataDelegate.setMetadataRequireSignature(false);
return extendedMetadataDelegate;
}
*/
@Bean
@Qualifier("idp-wso2is")
public ExtendedMetadataDelegate wso2IsExtendedMetadataProvider() throws Exception {
/*
String idpSSOCircleMetadataURL = env.getProperty("intranet.sso.idp.metadata.url");
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;
*/
String metadataName = env.getProperty("intranet.sso.idp.metadata.name");
if( logger.isInfoEnabled() ) {
logger.info("metadata name: {}", metadataName);
}
ClasspathResource idpMetadataResource = new ClasspathResource("/saml/"+metadataName);
ResourceBackedMetadataProvider metadataProvider =
new ResourceBackedMetadataProvider(new Timer(), idpMetadataResource);
metadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(metadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(false);
extendedMetadataDelegate.setMetadataRequireSignature(false);
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 Exception {
List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
//providers.add(ssoCircleExtendedMetadataProvider());
providers.add(wso2IsExtendedMetadataProvider());
return new CachingMetadataManager(providers);
}
// Filter automatically generates default SP metadata
@Bean
public MetadataGenerator metadataGenerator() {
MetadataGenerator metadataGenerator = new MetadataGenerator();
metadataGenerator.setEntityId(env.getProperty("intranet.sso.idp.metadata.entity.id"));
metadataGenerator.setExtendedMetadata(extendedMetadata());
metadataGenerator.setIncludeDiscoveryExtension(false);
metadataGenerator.setKeyManager(keyManager());
metadataGenerator.setBindingsSLO(Collections.singletonList("redirect"));
metadataGenerator.setSamlLogoutProcessingFilter(this.samlLogoutProcessingFilter());
metadataGenerator.setEntityBaseURL(env.getProperty("intranet.sso.local.metadata.bindings.base.url"));
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
.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("/webfonts/**").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();
}
}
Это то, что я получаю как ошибку:
2020-03-17 13:46:57.106 INFO centosx64 --- [ XNIO-1 task-1] o.s.s.s.l.SAMLDefaultLogger : LogoutReque
st;SUCCESS;161.27.16.60;it:eng:intranet:sso:sp:sviluppo:torre:remoto;https://wso2.login.am.sviluppo:9443;;;
2020-03-17 13:46:58.599 DEBUG centosx64 --- [ XNIO-1 task-6] o.s.s.s.SAMLProcessingFilter : Request is
to process authentication
2020-03-17 13:46:58.599 DEBUG centosx64 --- [ XNIO-1 task-6] o.s.s.s.SAMLProcessingFilter : Attempting
SAML2 authentication using profile urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser
2020-03-17 13:46:58.735 DEBUG centosx64 --- [ XNIO-1 task-6] o.s.s.s.p.SAMLProcessorImpl : Retrieving
message using binding urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
2020-03-17 13:46:58.736 INFO centosx64 --- [ XNIO-1 task-6] colMessageXMLSignatureSecurityPolicyRule : SAML protoc
ol message was not signed, skipping XML signature processing
2020-03-17 13:46:58.736 DEBUG centosx64 --- [ XNIO-1 task-6] o.s.s.s.u.SAMLUtil : Found endpo
int org.opensaml.saml2.metadata.impl.AssertionConsumerServiceImpl@10f8e3f3 for request URL http://intranet.sso.sviluppo.it:9191/intranetsso/saml/SSO based on location attribute in metadata
2020-03-17 13:46:58.736 INFO centosx64 --- [ XNIO-1 task-6] o.s.s.s.w.WebSSOProfileConsumerImpl : MESSAGE TYP
E org.opensaml.saml2.core.impl.LogoutResponseImpl TO STRING org.opensaml.saml2.core.impl.LogoutResponseImpl@6ba5fc33
2020-03-17 13:46:58.736 DEBUG centosx64 --- [ XNIO-1 task-6] o.s.s.s.SAMLAuthenticationProvider : Error valid
ating SAML message
org.opensaml.common.SAMLException: Message is not of a Response object type
at org.springframework.security.saml.websso.WebSSOProfileConsumerImpl.processAuthenticationResponse(WebSSOProfileConsumerImp
l.java:132)
at org.springframework.security.saml.SAMLAuthenticationProvider.authenticate(SAMLAuthenticationProvider.java:88)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)
at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:92)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationPro
cessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
Это что мне показывает springboot:
Я не могу понять, как избежать этой ошибки .... Кто-нибудь может подсказать мне, что мне не хватает?
Спасибо
Анджело