Spring session 与 spring security saml 的集成
Posted
技术标签:
【中文标题】Spring session 与 spring security saml 的集成【英文标题】:Integration of Spring session with spring security saml 【发布时间】:2016-12-03 22:43:30 【问题描述】:我想使用 openAm 对用户进行身份验证并共享会话以与其他微服务进行通信,而无需再次对用户进行身份验证。会话详细信息存储在外部数据库中。我正在使用带有所有 java 配置的 spring security saml 示例应用程序来实现这一点。 有两个会话正在生成。一个在登录 IDP 之前,一个在输入凭据之后。如果我在 openAm 检查,用户已登录,但在应用程序中,我收到 SAMLException,指定响应的 InResponseToField 与发送的消息 12345(随机 id)不对应。 如何将 spring session 与 saml 集成?
我的示例应用程序的安全配置文件
package com.vdenotaris.spring.boot.security.saml.web.config;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;
@Bean
public VelocityEngine velocityEngine()
return VelocityFactory.getEngine();
@Bean(initMethod = "initialize")
public StaticBasicParserPool parserPool()
return new StaticBasicParserPool();
@Bean(name = "parserPoolHolder")
public ParserPoolHolder parserPoolHolder()
return new ParserPoolHolder();
@Bean
public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager()
return new MultiThreadedHttpConnectionManager();
@Bean
public HttpClient httpClient()
return new HttpClient(multiThreadedHttpConnectionManager());
@Bean
public SAMLAuthenticationProvider samlAuthenticationProvider()
SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
samlAuthenticationProvider.setForcePrincipalAsString(false);
return samlAuthenticationProvider;
@Bean
public SAMLContextProviderImpl contextProvider()
return new SAMLContextProviderImpl();
@Bean
public static SAMLBootstrap sAMLBootstrap()
return new SAMLBootstrap();
@Bean
public SAMLDefaultLogger samlLogger()
return new SAMLDefaultLogger();
@Bean
public WebSSOProfileConsumer webSSOprofileConsumer()
return new WebSSOProfileConsumerImpl();
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer()
return new WebSSOProfileConsumerHoKImpl();
@Bean
public WebSSOProfile webSSOprofile()
return new WebSSOProfileImpl();
@Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOProfile()
return new WebSSOProfileConsumerHoKImpl();
@Bean
public WebSSOProfileECPImpl ecpprofile()
return new WebSSOProfileECPImpl();
@Bean
public SingleLogoutProfile logoutprofile()
return new SingleLogoutProfileImpl();
@Bean
public KeyManager keyManager()
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader
.getResource("classpath:/saml/keystore.jks");
String storePass = "password";
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mydomain", "password");
String defaultKey = "mydomain";
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
@Bean
public TLSProtocolConfigurer tlsProtocolConfigurer()
return new TLSProtocolConfigurer();
@Bean
public ProtocolSocketFactory socketFactory()
return new TLSProtocolSocketFactory(keyManager(), null, "default");
@Bean
public Protocol socketFactoryProtocol()
return new Protocol("https", socketFactory(), 443);
@Bean
public MethodInvokingFactoryBean socketFactoryInitialization()
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(Protocol.class);
methodInvokingFactoryBean.setTargetMethod("registerProtocol");
Object[] args = "https", socketFactoryProtocol();
methodInvokingFactoryBean.setArguments(args);
return methodInvokingFactoryBean;
@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;
@Bean
public ExtendedMetadata extendedMetadata()
ExtendedMetadata extendedMetadata = new ExtendedMetadata();
extendedMetadata.setIdpDiscoveryEnabled(true);
extendedMetadata.setSignMetadata(false);
return extendedMetadata;
@Bean
public SAMLDiscovery samlIDPDiscovery()
SAMLDiscovery idpDiscovery = new SAMLDiscovery();
idpDiscovery.setIdpSelectionPath("/saml/idpSelection");
return idpDiscovery;
@Bean
@Qualifier("idp-ssocircle")
public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider()
throws MetadataProviderException
String idpSSOCircleMetadataURL = "http://openam.com:8080/OpenAM/saml2/jsp/exportmetadata.jsp";
Timer backgroundTaskTimer = new Timer(true);
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
backgroundTaskTimer, httpClient(), idpSSOCircleMetadataURL);
httpMetadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(true);
extendedMetadataDelegate.setMetadataRequireSignature(false);
return extendedMetadataDelegate;
@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("http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata");
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("/");
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;
@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());
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler()
SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl("/");
return successLogoutHandler;
@Bean
public SecurityContextLogoutHandler logoutHandler()
SecurityContextLogoutHandler logoutHandler =
new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(true);
logoutHandler.setClearAuthentication(true);
return logoutHandler;
@Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter()
return new SAMLLogoutProcessingFilter(successLogoutHandler(),
logoutHandler());
@Bean
public SAMLLogoutFilter samlLogoutFilter()
return new SAMLLogoutFilter(successLogoutHandler(),
new LogoutHandler[] logoutHandler() ,
new LogoutHandler[] logoutHandler() );
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);
@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);
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception
return super.authenticationManagerBean();
@Override
protected void configure(HttpSecurity http) throws Exception
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.csrf()
.disable();
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(sessionRepositoryFilter(sessionRepository(), httpSessionStrategy()),
ChannelProcessingFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/publicUrl").permitAll()
.antMatchers("/app/**").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/saml/**").permitAll()
.antMatchers("/landing").authenticated();
http
.logout()
.logoutSuccessUrl("/");
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth
.authenticationProvider(samlAuthenticationProvider());
@Bean
public HttpSessionStrategy httpSessionStrategy()
return new HeaderHttpSessionStrategy();
@Bean
public SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter(
SessionRepository<ExpiringSession> sessionRepository, HttpSessionStrategy httpSessionStrategy)
SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter = new SessionRepositoryFilter<>(
sessionRepository);
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
return sessionRepositoryFilter;
@Bean
public SessionRepository<ExpiringSession> sessionRepository()
return new JPASessionRepository(1800);
实现 SessionRepository 的 JPA Session 存储库是:
package com.security.repositories.session;
public class JPASessionRepository implements SessionRepository<ExpiringSession>
private static final Logger LOG = LogManager.getLogger(JPASessionRepository.class);
private int maxInactiveInterval = -1;
@Autowired
private SpringSessionRepository springSessionRepository;
public JPASessionRepository()
public JPASessionRepository(int maxInactiveInterval)
this.maxInactiveInterval = maxInactiveInterval;
@Override
public ExpiringSession createSession()
ExpiringSession result = new MapSession();
result.setMaxInactiveIntervalInSeconds(maxInactiveInterval);
return result;
@Transactional
@Override
public void save(ExpiringSession session)
springSessionRepository.save(convertToDomain(session));
@Transactional
@Override
public ExpiringSession getSession(String id)
SessionEntity sessionEntity = springSessionRepository.findOne(id);
ExpiringSession saved = null;
if(sessionEntity != null)
saved = convertToSession(sessionEntity);
if (saved == null)
return null;
if (saved.isExpired())
delete(saved.getId());
return null;
return saved;
@Override
public void delete(String id)
SessionEntity currentSession = springSessionRepository.findOne(id);
if (null != currentSession)
springSessionRepository.delete(id);
private SessionEntity convertToDomain(ExpiringSession session)
SessionEntity sessionEntity = new SessionEntity();
sessionEntity.setId(session.getId());
sessionEntity.setLastAccessedTime(session.getLastAccessedTime());
sessionEntity.setCreationTime(session.getCreationTime());
sessionEntity.setData(serializeAttributes(session));
return sessionEntity;
byte[] serializeAttributes(ExpiringSession session)
Map<String, Object> attributes = new HashMap<>();
for (String attrName : session.getAttributeNames())
attributes.put(attrName, session.getAttribute(attrName));
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer;
try
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(new SessionAttributes(attributes));
buffer = out.toByteArray();
objectOutputStream.close();
catch (IOException e)
throw new RuntimeException(e);
return buffer;
private ExpiringSession convertToSession(SessionEntity sessionEntity)
MapSession mapSession = new MapSession();
mapSession.setId(sessionEntity.getId());
mapSession.setLastAccessedTime(sessionEntity.getLastAccessedTime());
mapSession.setCreationTime(sessionEntity.getCreationTime());
mapSession.setMaxInactiveIntervalInSeconds(this.maxInactiveInterval);
SessionAttributes attributes = deserializeAttributes(sessionEntity);
if (attributes != null)
for (Map.Entry<String, Object> attribute : attributes.getAttributes().entrySet())
mapSession.setAttribute(attribute.getKey(), attribute.getValue());
return mapSession;
private SessionAttributes deserializeAttributes(SessionEntity sessionEntity)
SessionAttributes attributes = null;
if (sessionEntity.getData() != null && sessionEntity.getData().length > 0)
try
ObjectInputStream objectInputStream = new ObjectInputStream(
new ByteArrayInputStream(sessionEntity.getData()));
Object obj = objectInputStream.readObject();
attributes = (SessionAttributes) obj;
objectInputStream.close();
catch (IOException | ClassNotFoundException e)
LOG.warn(e);
//FIXME:How should this exception be handled?
return attributes;
public Integer getDefaultMaxInactiveInterval()
return maxInactiveInterval;
public void setDefaultMaxInactiveInterval(int maxInactiveInterval)
this.maxInactiveInterval = maxInactiveInterval;
public SpringSessionRepository getSpringSessionRepository()
return springSessionRepository;
public void setSpringSessionRepository(SpringSessionRepository springSessionRepository)
this.springSessionRepository = springSessionRepository;
SP 元数据
<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="http___localhost_8080_spring-boot-security-saml2-sample_saml_web_metadata" entityID="http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata"><md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>x509 certificate data</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:KeyDescriptor use="encryption"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>--x509 certificate</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SingleLogout"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/saml/SingleLogout"/><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SSO" index="0" isDefault="true"/><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="http://localhost:8080/saml/SSO" index="1"/></md:SPSSODescriptor></md:EntityDescriptor>
IDP 元数据:-
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8081/OpenAM">
<IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
sample certificate
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<ArtifactResolutionService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/ArtifactResolver/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPSloSoap/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPMniSoap/metaAlias/idp"/>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/SSORedirect/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/SSOPOST/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/SSOSoap/metaAlias/idp"/>
<NameIDMappingService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/NIMSoap/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/AIDReqSoap/IDPRole/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://localhost:8081/OpenAM/AIDReqUri/IDPRole/metaAlias/idp"/>
</IDPSSODescriptor>
</EntityDescriptor>
请帮我解决问题
【问题讨论】:
请发布您的 IDP 和 SP 元数据。 @blur0224 更新了 SP 和 IDP 元数据 你检查过这个吗? github.com/ulisesbocchio/spring-boot-security-saml。使 Spring Boot 和 SAML 之间的配置更容易 【参考方案1】:此错误通常是由于 EntityID 以某种方式不匹配造成的。看起来您的元数据配置正确,所以我做了一些查找并找到了this。
确保应用程序在发送请求和接收响应期间使用相同的 HttpSession。通常,当从 localhost 地址或 http 方案初始化身份验证请求时会出现此问题,而在公共主机名或 https 方案中接收到响应时。例如,从 URL https://host:port/app/saml/login 初始化身份验证时,必须在https://host;port/app/saml/SSO 接收响应,而不是http://host:port/app/saml/SSO 或https://localhost:port/app/saml/SSO。
可以通过重新配置上下文提供程序来禁用 InResponseToField 的检查,如下所示:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl">
<property name="storageFactory">
<bean class="org.springframework.security.saml.storage.EmptyStorageFactory"/>
</property>
</bean>
您应该注意,这只能用于开发目的。您可能应该使用Spring Profiles 在本地启用此配置,因为它不太安全。
【讨论】:
以上是关于Spring session 与 spring security saml 的集成的主要内容,如果未能解决你的问题,请参考以下文章
Spring session 与 spring security saml 的集成
帅气的 Spring Session 功能,基于 Redis 实现分布式会话,还可以整合 Spring Security!