I recently encountered a problem with using springSAML 2.0 in a java project. The "problem" looks very "simple". I also tried to consult the literature and follow the tips on the article but it still does not work.
The network environment in which my program is located is very complex because it is a commercial system for the company. Therefore, the java program is deployed under F5 load balancing. I have already paid attention to setting the class of "contextProvider" to "SAMLContextProviderLB", but the problem It should not be here.
I checked it out:
Error on implementing spring-saml-security with WSO2
I've set: to lazy-init = false, but it still does not work.
I also checked:
SAML authentication is failing with 'MetadataKeyInfoGenerator' does not exist after enabling AXIS2 #200
Although I also suspect that Axis2.0 problem, but I do have no evidence of this problem.
I have two machines deployed two java applications, and these two java applications are open springSAML function, of course, I have generated a keystore configured correctly (but I use the same two keystore two applications, because I The configuration is exactly the same), the two machines after I stop and then start I visit samlLogin are ok, and then I found a customer complained after sleeping, said idp select interface appeared 500 errors, I looked found:
java.lang.IllegalArgumentException: Manager with name 'MetadataKeyInfoGenerator' does not exist
org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager.getFactory (NamedKeyInfoGeneratorManager.java:156)
org.opensaml.xml.security.SecurityHelper.getKeyInfoGenerator (SecurityHelper.java:1049)
org.springframework.security.saml.metadata.MetadataGenerator.generateKeyInfoForCredential (MetadataGenerator.java:255)
org.springframework.security.saml.metadata.MetadataGenerator.getServerKeyInfo (MetadataGenerator.java: 211)
org.springframework.security.saml.metadata.MetadataGenerator.buildSPSSODescriptor (MetadataGenerator.java:329)
org.springframework.security.saml.metadata.MetadataGenerator.generateMetadata (MetadataGenerator.java:189)
org.springframework.security.saml.metadata.MetadataGeneratorFilter.processMetadataInitialization (MetadataGeneratorFilter.java:127)
org.springframework.security.saml.metadata.MetadataGeneratorFilter.doFilter (MetadataGeneratorFilter.java:86)
org.springframework.security.web.FilterChainProxy $ VirtualFilterChain.doFilter (FilterChainProxy.java:342)
org.springframework.security.web.FilterChainProxy.doFilterInternal (FilterChainProxy.java:192)
org.springframework.security.web.FilterChainProxy.doFilter (FilterChainProxy.java:160)
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate (DelegatingFilterProxy.java:343)
org.springframework.web.filter.DelegatingFilterProxy.doFilter (DelegatingFilterProxy.java:260)
I know this question is DefaultBootstrap does not seem to start, I do not know when Spring will start it, I tracked the source found because:
public class NamedKeyInfoGeneratorManager
{
private Map <String, KeyInfoGeneratorManager> managers;
There's nothing inside the managers.
I was thinking why the managers could not find 'MetadataKeyInfoGenerator'.
Please help me.
my java project web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>SSO</display-name>
<!--分割线|在启用saml模式下打开配置|start -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/securityContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/saml/*</url-pattern>
<url-pattern>/samlLogin</url-pattern>
</filter-mapping>
<!-- 分割线|在启用saml模式下打开配置 |end-->
......
<servlet>
<servlet-name>AxisServlet</servlet-name>
<servlet-class>org.apache.axis2.transport.http.AxisServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>/servlet/AxisServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AxisServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LoggingConfigure</servlet-name>
<servlet-class>com.ztesoft.zsmart.web.util.Log4jConfigServlet</servlet-class>
<init-param>
<param-name>log4jExposeWebAppRoot</param-name>
<param-value>CVBS_WEBAPP_REALPATH</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>MVCServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-application-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>/oauth/2.0/authorize/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>/oauth/2.0/token/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>/oauth/2.0/token/validate/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MVCServlet</servlet-name>
<url-pattern>/oauth/2.0/user/getInfo/*</url-pattern>
</servlet-mapping>
<servlet>
<display-name>WebInitServlet</display-name>
<servlet-name>WebInitServlet</servlet-name>
<servlet-class>com.ztesoft.zsmart.sso.web.servlet.WebInitServlet</servlet-class>
<init-param>
<param-name>DEFAULT_LANGUAGE</param-name>
<param-value>en-US</param-value>
</init-param>
<init-param>
<param-name>LOCAL_CHARSET</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>DEFAULT_CHARSET</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>REQUEST_ENCODE</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
<load-on-startup>100</load-on-startup>
</servlet>
<!-- saml 功能开启是打开-->
<servlet>
<servlet-name>samlLoginServlet</servlet-name>
<servlet-class>
com.ztesoft.zsmart.sso.web.servlet.SamlLoginServlet
</servlet-class>
<init-param>
<param-name>serviceUrl</param-name>
<param-value>/saml/logout</param-value>
</init-param>
<init-param>
<param-name>path</param-name>
<param-value>/</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>samlLoginServlet</servlet-name>
<url-pattern>/samlLogin</url-pattern>
</servlet-mapping>
<servlet>
<description>Common Servlet for Download</description>
<display-name>Common Servlet for Download</display-name>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>com.ztesoft.zsmart.web.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/DownloadServlet.do</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
<welcome-file-list>
<welcome-file>LoginIndex.do</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>trimSpaces</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<error-page>
<error-code>404</error-code>
<location>/Error.do?SSO_ERROR_CODE=S-SSO-10044</location>
</error-page>
<servlet>
<description></description>
<display-name>OauthAppAPI</display-name>
<servlet-name>OauthAppAPI</servlet-name>
<servlet-class>com.ztesoft.zsmart.sso.oauth.v2.as.api.OauthAppAPI</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OauthAppAPI</servlet-name>
<url-pattern>/OauthAppAPI</url-pattern>
</servlet-mapping>
</web-app>
my securityContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Enable auto-wiring -->
<context:annotation-config/>
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- 配置不拦截资源 -->
<security:http security="none" pattern="/webportal/**"/>
<security:http security="none" pattern="/webtechfrm/**"/>
<security:http security="none" pattern="/sso/**"/>
<security:http security="none" pattern="/oauth/**"/>
<security:http security="none" pattern="/*.do"/>
<security:http security="none" pattern="/*/*/*.do"/>
<security:http security="none" pattern="/*/*/*/*.do"/>
<security:http security="none" pattern="/Login.jsp*"/>
<security:http security="none" pattern="/metadata/**"/>
<security:http security="none" pattern="/services/**"/>
<!--配置节点 -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!--SAML处理拦截器-->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/samlLogin"/>
</bean>
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/Error.do?SSO_ERROR_CODE=P_ADFS_LOGINERROR"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<!--<property name="defaultTargetUrl" value="/LoginIndex.do"/>-->
</bean>
<security:authentication-manager alias="authenticationManager">
<!-- Register authentication manager for SAML provider -->
<security:authentication-provider ref="samlAuthenticationProvider"/>
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- 配置SSL证书,需要在部署之前配置好 -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="/WEB-INF/tomcat/tomcat-ssl.keystore"/>
<constructor-arg type="java.lang.String" value="ztesoft"/>
<constructor-arg>
<map>
<entry key="tomcat7" value="ztesoft"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="tomcat7"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
<!--
<property name="binding" value="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"/>
-->
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/idpSelection.jsp"/>
</bean>
<!-- 该过滤器希望对已配置的URL进行调用,并向用户提供表示该应用程序部署的SAML2元数据。 -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="signMetadata" value="false"/>
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
<property name="entityBaseURL" value="https://webportalapp.bes.post.lu:8440/sso"/>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<!-- 配置IDP Metadata,可配置多个 -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<!-- ADFS -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
<constructor-arg>
<bean class="org.opensaml.util.resource.FilesystemResource">
<constructor-arg value="/sso/cer/post/federationmetadata.xml"/>
</bean>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
</list>
</constructor-arg>
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
</bean>
<!-- Provider of default SAML Context -->
<!--<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>-->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="https"/>
<property name="serverName" value="webportalapp.bes.post.lu"/>
<property name="serverPort" value="8440"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/sso"/>
</bean>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- 初始化 OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap" lazy-init="false"/>
<!-- 初始化搜索引擎 -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!-- OpenSAML解析所需的XML解析器池-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
</beans>
Related
I'm setting up OKTA SAML 2.0 integration with Struts 1.0 in Wildfly 8.1.0. Earlier I had the same setup in JBoss 7 server in which everything works fine. I just replicated the same in Wildfly 8 but after the authentication is completed in OKTA and the web page is redirecting to my application in which
request.getSession(false)
returns null.
Here is my SecurityFilter.java
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession(false);
}
JBoss-WEb.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<!-- local -->
<context-root>application</context-root>
<!-- production
<context-root>/</context-root> -->
</jboss-web>
SecurityContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.springframework.security.saml"/>
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<!--<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFiler"/> -->
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</security:filter-chain-map>
</bean>
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/login.do?method=home"/>
</bean>
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="false"/>
</bean>
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="samlAuthenticationProvider"/>
<security:authentication-provider>
<security:user-service id="adminInterfaceService">
<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="nalle123"/>
<constructor-arg>
<map>
<entry key="apollo" value="nalle123"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="apollo"/>
</bean>
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/idpSelection.jsp"/>
</bean>
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">classpath:security/idp.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
</bean>
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!-- XML parser pool needed for OpenSAML parsing -->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize">
<property name="builderFeatures">
<map>
<entry key="http://apache.org/xml/features/dom/defer-node-expansion" value="false"/>
</map>
</property>
</bean>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
</beans>
If the authentication is redirected to
http://localhost:8080/application
then there is value in the session
but when I try the same in production URL
http://www.myproductionsite.com:8080/
then I'm getting null pointer exception at
request.getSession(false);
You would have encountered this issue in Chrome v80 and above. Chrome now requires the session cookie to have samesite=NONE attribute if our application allows other domains such as Okta to POST data on the path /saml/SSO. This feature exists to protect applications against CSRF
If the cookie doesn't have the samesite=NONE attribute, Chrome will not include this cookie with the request Okta posts making your application see this request as a completely new request explaining the request.getSession(false) value to be null
Please see more here - https://support.okta.com/help/s/article/FAQ-How-Chrome-80-Update-for-SameSite-by-default-Potentially-Impacts-Your-Okta-Environment?language=en_US
There are different options to provide the sameSite attribute. The cleanest I think is by using spring session and providing a cookie serializer bean or using the latest release of spring session.
See this link for more options - Same-Site cookie in Spring Security
I've read several blogs about this subject and all the stackoverflow.com articles I can get my hands on, but nothing seems to answer the problem I've been encountering.
I've followed several Okta examples online, including the main blog post that was posted this year. I still run into the issue below, which when I trace through the spring-security code gets me to the voting process in spring and it fails.
TRACE 2017-08-22 16:45:11,128 [org.springframework.web.context.support.XmlWebApplicationContext] - Publishing event in Root WebApplicationContext: org.springframework.security.access.event.AuthorizationFailureEvent[source=FilterInvocation: URL: /SSO]
DEBUG 2017-08-22 16:45:11,129 [org.springframework.security.web.access.ExceptionTranslationFilter] - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206)
In my setup I'm not using spring boot or spring annotations, but rather good ol' spring xml configuration. I've look at the configuration enough that I don't see where the issue lies.
versions:
spring: 3.2.18.RELEASE
spring security: 3.2.7.RELEASE
spring saml2: 1.0.2.RELEASE
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- Enable auto-wiring
<context:annotation-config/>-->
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/style/**"/>
<security:http security="none" pattern="/scripts/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<!-- Security for the administration UI -->
<security:http pattern="/saml/web/**" use-expressions="false">
<security:access-denied-handler error-page="/saml/web/metadata/login"/>
<security:form-login login-processing-url="/saml/web/login" login-page="/saml/web/metadata/login" default-target-url="/saml/web/metadata"/>
<security:intercept-url pattern="/saml/web/metadata/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/saml/web/**" access="ROLE_ADMIN"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>
<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<!-- <security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/> -->
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/app/buildingInformation"/>
</bean>
<!--
Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
<property name="defaultTargetUrl" value="/" />
</bean>-->
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/error.jsp"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/logout.jsp"/>
</bean>
<!-- <bean id="mySecurityUserService" class="com.my.security.service.MySecurityUserService">
<property name="application" ref="application"/>
<property name="allowAllAccess" ref="allowAllAccess"/>
</bean> -->
<security:authentication-manager alias="authenticationManager">
<!-- Register authentication manager for SAML provider -->
<security:authentication-provider ref="samlAuthenticationProvider"/>
<!-- Register authentication manager for administration UI
<security:authentication-provider user-service-ref="mySecurityUserService"/>-->
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/mykeystore.jks"/>
<constructor-arg type="java.lang.String" value="secret"/>
<constructor-arg>
<map>
<entry key="spring" value="secret"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="spring"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<!-- <property name="idpDiscoveryEnabled" value="true"/> -->
<property name="idpDiscoveryEnabled" value="false"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<!-- Configure HTTP Client to accept certificates from the keystore for HTTPS verification -->
<!--
<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="sslHostnameVerification" value="default"/>
</bean>
-->
<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<!-- Example of classpath metadata with Extended Metadata -->
<bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
<constructor-arg>
<bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
<constructor-arg>
<bean class="java.util.Timer"/>
</constructor-arg>
<constructor-arg>
<bean class="org.opensaml.util.resource.ClasspathResource">
<constructor-arg value="/metadata/idp.xml"/>
</bean>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
</bean>
</constructor-arg>
</bean>
<!-- Example of HTTP metadata without Extended Metadata -->
<!-- <bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
URL containing the metadata
<constructor-arg>
<value type="java.lang.String">https://dev-738438.oktapreview.com/app/exkbq3s0o4HnCFf3U0h7/sso/saml/metadata</value>
</constructor-arg>
Timeout for metadata loading in ms
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean> -->
<!-- Example of file system metadata without Extended Metadata -->
<!--
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">/usr/local/metadata/idp.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
-->
</list>
</constructor-arg>
<!-- OPTIONAL used when one of the metadata files contains information about this service provider -->
<!-- <property name="hostedSPName" value=""/> -->
<!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
<!-- <property name="defaultIDP" value="http://localhost:8080/opensso"/> -->
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login
<property name="userDetails" ref="mySecurityUserService"/> -->
</bean>
<!-- Provider of default SAML Context -->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!--
XML parser pool needed for OpenSAML parsing
WARNING: If customizing a ParserPool implementation See https://shibboleth.net/community/advisories/secadv_20131213.txt
Specifically the following should be explicitly set to avoid exploits:
1) set pool property 'expandEntityReferences' to 'false'
2) set feature 'javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING' to true
3) set feature 'http://apache.org/xml/features/disallow-doctype-decl' to true. This is a Xerces-specific feature,
including derivatives such as the internal JAXP implementations supplied with the Oracle and OpenJDK JREs. For
other JAXP implementations, consult the documentation for the implementation for guidance on how to achieve a
similar configuration.
-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
Thanks in advance for any help or for pointing me in the right direction.
This guide is a bit old, but it shows how to make things work with good ol' Spring XML: https://developer.okta.com/code/java/spring_security_saml.html
I am using the Spring Saml Sample application but with a load balancer in front. I have change the contextProvider as following. Requests are being redirected to IDP and after authentication, I keep getting error "Caused by: org.opensaml.common.SAMLException: InResponseToField of the Response doesn't correspond to sent message a1agabe66iba055b26j9i0gac8ah48h"
Can someone help me out please.
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Enable auto-wiring -->
<context:annotation-config/>
<!-- Scan for auto-wiring classes in spring saml packages -->
<context:component-scan base-package="org.springframework.security.saml"/>
<!-- Unsecured pages -->
<security:http security="none" pattern="/favicon.ico"/>
<security:http security="none" pattern="/images/**"/>
<security:http security="none" pattern="/css/**"/>
<security:http security="none" pattern="/logout.jsp"/>
<!-- Security for the administration UI -->
<security:http pattern="/saml/web/**" use-expressions="false">
<security:access-denied-handler error-page="/saml/web/metadata/login"/>
<security:form-login login-processing-url="/saml/web/login" login-page="/saml/web/metadata/login" default-target-url="/saml/web/metadata"/>
<security:intercept-url pattern="/saml/web/metadata/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/saml/web/**" access="ROLE_ADMIN"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
</security:http>
<!-- Secured pages with SAML as entry point -->
<security:http entry-point-ref="samlEntryPoint" use-expressions="false">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</security:http>
<!-- Filters for processing of SAML messages -->
<bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map request-matcher="ant">
<security:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<security:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<security:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<security:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<security:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<security:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<security:filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</security:filter-chain-map>
</bean>
<!-- Handler deciding where to redirect user after successful login -->
<bean id="successRedirectHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
<!--
Use the following for interpreting RelayState coming from unsolicited response as redirect URL:
<bean id="successRedirectHandler" class="org.springframework.security.saml.SAMLRelayStateSuccessHandler">
<property name="defaultTargetUrl" value="/" />
</bean>
-->
<!-- Handler deciding where to redirect user after failed login -->
<bean id="failureRedirectHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="useForward" value="true"/>
<property name="defaultFailureUrl" value="/error.jsp"/>
</bean>
<!-- Handler for successful logout -->
<bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<property name="defaultTargetUrl" value="/logout.jsp"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<!-- Register authentication manager for SAML provider -->
<security:authentication-provider ref="samlAuthenticationProvider"/>
<!-- Register authentication manager for administration UI -->
<security:authentication-provider>
<security:user-service id="adminInterfaceService">
<security:user name="admin" password="admin" authorities="ROLE_ADMIN"/>
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<!-- Logger for SAML messages and events -->
<bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/>
<!-- Central storage of cryptographic keys -->
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
<constructor-arg value="classpath:security/samlKeystore.jks"/>
<constructor-arg type="java.lang.String" value="nalle123"/>
<constructor-arg>
<map>
<entry key="apollo" value="nalle123"/>
</map>
</constructor-arg>
<constructor-arg type="java.lang.String" value="apollo"/>
</bean>
<!-- Entry point to initialize authentication, default values taken from properties file -->
<bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint">
<property name="defaultProfileOptions">
<bean class="org.springframework.security.saml.websso.WebSSOProfileOptions">
<property name="includeScoping" value="false"/>
</bean>
</property>
</bean>
<!-- IDP Discovery Service -->
<bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDiscovery">
<property name="idpSelectionPath" value="/WEB-INF/security/idpSelection.jsp"/>
</bean>
<!-- Filter automatically generates default SP metadata -->
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there -->
<bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/>
<!-- Configure HTTP Client to accept certificates from the keystore for HTTPS verification -->
<!--
<bean class="org.springframework.security.saml.trust.httpclient.TLSProtocolConfigurer">
<property name="sslHostnameVerification" value="default"/>
</bean>
-->
<!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here -->
<bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
<constructor-arg>
<list>
<!-- Example of classpath metadata with Extended Metadata -->
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<!-- URL containing the metadata -->
<constructor-arg>
<value type="java.lang.String">http://localhost/idp/idp-meta.xml</value>
</constructor-arg>
<!-- Timeout for metadata loading in ms -->
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
<!-- Example of HTTP metadata without Extended Metadata -->
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<!-- URL containing the metadata -->
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<!-- Timeout for metadata loading in ms -->
<constructor-arg>
<value type="int">15000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
<!-- Example of file system metadata without Extended Metadata -->
<!--
<bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider">
<constructor-arg>
<value type="java.io.File">/usr/local/metadata/idp.xml</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
-->
</list>
</constructor-arg>
<!-- OPTIONAL used when one of the metadata files contains information about this service provider -->
<!-- <property name="hostedSPName" value=""/> -->
<!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
<!-- <property name="defaultIDP" value="http://localhost:8080/opensso"/> -->
</bean>
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login -->
<!--
<property name="userDetails" ref="bean" />
-->
</bean>
<!-- Provider of default SAML Context -->
-->
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB">
<property name="scheme" value="http"/>
<property name="serverName" value="localhost"/>
<property name="includeServerPortInRequestURL" value="false"/>
<property name="contextPath" value="/spring-security-saml2-sample"/>
</bean>
<!-- Processing filter for WebSSO profile messages -->
<bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Processing filter for WebSSO Holder-of-Key profile -->
<bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationSuccessHandler" ref="successRedirectHandler"/>
<property name="authenticationFailureHandler" ref="failureRedirectHandler"/>
</bean>
<!-- Logout handler terminating local session -->
<bean id="logoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
<property name="invalidateHttpSession" value="false"/>
</bean>
<!-- Override default logout processing filter with the one processing SAML messages -->
<bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
<constructor-arg index="2" ref="logoutHandler"/>
</bean>
<!-- Filter processing incoming logout messages -->
<!-- First argument determines URL user will be redirected to after successful global logout -->
<bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter">
<constructor-arg index="0" ref="successLogoutHandler"/>
<constructor-arg index="1" ref="logoutHandler"/>
</bean>
<!-- Class loading incoming SAML messages from httpRequest stream -->
<bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg>
<list>
<ref bean="redirectBinding"/>
<ref bean="postBinding"/>
<ref bean="artifactBinding"/>
<ref bean="soapBinding"/>
<ref bean="paosBinding"/>
</list>
</constructor-arg>
</bean>
<!-- SAML 2.0 WebSSO Assertion Consumer -->
<bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/>
<!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer -->
<bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 Web SSO profile -->
<bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/>
<!-- SAML 2.0 Holder-of-Key Web SSO profile -->
<bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/>
<!-- SAML 2.0 ECP profile -->
<bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/>
<!-- SAML 2.0 Logout Profile -->
<bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/>
<!-- Bindings, encoders and decoders used for creating and parsing messages -->
<bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
</bean>
<bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding">
<constructor-arg ref="parserPool"/>
<constructor-arg ref="velocityEngine"/>
<constructor-arg>
<bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl">
<constructor-arg>
<bean class="org.apache.commons.httpclient.HttpClient">
<constructor-arg>
<bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</constructor-arg>
</bean>
</constructor-arg>
<property name="processor">
<bean class="org.springframework.security.saml.processor.SAMLProcessorImpl">
<constructor-arg ref="soapBinding"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
<bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding">
<constructor-arg ref="parserPool"/>
</bean>
<!-- Initialization of OpenSAML library-->
<bean class="org.springframework.security.saml.SAMLBootstrap"/>
<!-- Initialization of the velocity engine -->
<bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/>
<!--
XML parser pool needed for OpenSAML parsing
WARNING: If customizing a ParserPool implementation See https://shibboleth.net/community/advisories/secadv_20131213.txt
Specifically the following should be explicitly set to avoid exploits:
1) set pool property 'expandEntityReferences' to 'false'
2) set feature 'javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING' to true
3) set feature 'http://apache.org/xml/features/disallow-doctype-decl' to true. This is a Xerces-specific feature,
including derivatives such as the internal JAXP implementations supplied with the Oracle and OpenJDK JREs. For
other JAXP implementations, consult the documentation for the implementation for guidance on how to achieve a
similar configuration.
-->
<bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"/>
<bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/>
I found the solution for problem. It was explained very well at Spring SAML integration with WSO2 Identity server, SAML Message ID not reconised
I went with updating the cookie name approach using org.springframework.session.web.http.CookieHttpSessionStrategy.
<bean id="sessionRepositoryFilter"
class="org.springframework.session.web.http.SessionRepositoryFilter">
<constructor-arg ref="sessionRepository"/>
<property name="httpSessionStrategy">
<bean class="org.springframework.session.web.http.CookieHttpSessionStrategy">
<property name="cookieName" value="myCookieName" />
</bean>
</property>
</bean>
I am seeking solutions to migrate current holistic system into microservices architecture. I want to use Spring Integration and Spring Security to integrate and secure the services. According to my understanding, to secure backend services is more like Single Sign On (SSO). I use Jasig CAS 4.2.7 (seems working fine with Spring Security) to authenticate users centrally, Spring Integration 4.2.11.RELEASE and Spring Security 4.0.4.RELEASE.
I have created a Maven project with two modules named web and service which are both web application. I deploy the three war files on same local Tomcat (version 7.0.36) and just add jimi and bob into CAS properties file to ensure them passing the authentication of CAS. When I try to access URL http://localhost:8080/prototype-integration-security-web/user, I got authenticated into front-end application but access forbidden on backend services.
The POM file looks as below.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>prototype.integration.security</groupId>
<artifactId>prototype-integration-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>prototype-integration-security</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warName>${project.name}</warName>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-http</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-security</artifactId>
<version>4.2.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>4.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
<modules>
<module>prototype-integration-security-web</module>
<module>prototype-integration-security-service</module>
</modules>
</project>
The deployment description files web.xml of two modules look same except the display name as following.
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="IntegrationSecurityWeb" version="3.0">
<display-name>Integration Security Web Prototype</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
In the Spring application context configuration file of web module, dispatcher-servlet.xml looks as below.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-web/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="http://localhost:8080/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern="user*" send-access="ROLE_USER" />
<int-security:access-policy pattern="admin*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/user*" supported-methods="GET, POST" channel="userRequestChannel" />
<int:channel id="userRequestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-service/query?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="userRequestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials"/>
</int-http:outbound-channel-adapter>
<int-http:inbound-channel-adapter path="/admin/callback*"
supported-methods="GET, POST"
channel="adminRequestChannel" />
<int:channel id="adminRequestChannel">
<int:queue/>
</int:channel>
<int:logging-channel-adapter id="logging" channel="adminRequestChannel" level="DEBUG" />
</beans>
In the context configuration file of service module, dispatcher-servlet.xml looks as following.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-4.2.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http-4.2.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-4.2.xsd">
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg>
<bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<constructor-arg>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.apache.http.impl.client.HttpClients"/>
<property name="targetMethod" value="createMinimal"/>
</bean>
</constructor-arg>
</bean>
</constructor-arg>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter">
</bean>
</list>
</property>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/prototype-integration-security-service/login/cas" />
<property name="sendRenew" value="false" />
</bean>
<!-- Access voters -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg>
<bean class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
</value>
</property>
</bean>
</constructor-arg>
</bean>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
<!-- <bean class="org.springframework.security.web.access.expression.WebExpressionVoter" /> -->
</list>
</constructor-arg>
</bean>
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login" />
<property name="serviceProperties" ref="serviceProperties" />
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" />
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:8443/cas/logout" />
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
</constructor-arg>
<property name="filterProcessesUrl" value="/logout/cas" />
</bean>
<security:http entry-point-ref="casEntryPoint" access-decision-manager-ref="accessDecisionManager" use-expressions="false">
<security:intercept-url pattern="/**" access="ROLE_ADMIN"/>
<security:form-login />
<security:logout />
<security:custom-filter before="LOGOUT_FILTER" ref="requestSingleLogoutFilter"/>
<security:custom-filter before="CAS_FILTER" ref="singleLogoutFilter"/>
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<security:user-service id="userService">
<security:user name="jimi" password="jimi" authorities="ROLE_ADMIN" />
<security:user name="bob" password="bob" authorities="ROLE_USER" />
</security:user-service>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg index="0" ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="localCAS" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<int:channel-interceptor order="99">
<bean class="org.springframework.integration.security.channel.SecurityContextPropagationChannelInterceptor"/>
</int:channel-interceptor>
<task:executor id="pool" pool-size="5"/>
<int:poller id="poller" default="true" fixed-rate="1000"/>
<int-security:secured-channels>
<int-security:access-policy pattern=".*" send-access="ROLE_ADMIN" />
</int-security:secured-channels>
<int-http:inbound-channel-adapter path="/query*" supported-methods="GET, POST" channel="requestChannel" />
<int:channel id="requestChannel">
<int:queue/>
</int:channel>
<int-http:outbound-channel-adapter url="http://localhost:8080/prototype-integration-security-web/admin/callback?ticket={ticket}"
http-method="GET"
rest-template="restTemplate"
channel="requestChannel">
<int-http:uri-variable name="ticket" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials" />
</int-http:outbound-channel-adapter>
</beans>
No additional code is required, this is why I am fond of Spring Integration. Did I do anything wrong or miss some configurations? Please share your ideas, opinions and suggestions. Thanks in advance.
I have never used CAS before, but looks like you don't share how you get headers.serviceTicket.
I think you idea to propagate ticket via URL param is good, but first of all we have to extract it from the incoming URL:
Upon successful login, CAS will redirect the user’s browser back to the original service. It will also include a ticket parameter, which is an opaque string representing the "service ticket". Continuing our earlier example, the URL the browser is redirected to might be https://server3.company.com/webapp/login/cas?ticket=ST-0-ER94xMJmn6pha35CQRoZ.
http://docs.spring.io/spring-security/site/docs/4.2.0.RELEASE/reference/htmlsingle/#cas
For this purpose we can do like this:
<int-http:inbound-channel-adapter path="/user*" supported-methods="GET, POST" channel="userRequestChannel">
<int-http:header name="serviceTicket" expression="#requestParams.ticket"/>
</int-http:inbound-channel-adapter>
Otherwise, please, share exception on the matter and try to trace network traffic to determine a gap.
UPDATE
According to the description on that Spring Security page for CAS we have:
... The principal will be equal to CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, whilst the credentials will be the service ticket opaque value...
So, looks like we don't need to worry about request param in the <int-http:inbound-channel-adapter> and just rely on the SecurityContext in the <int-http:outbound-gateway>:
<int-http:uri-variable name="ticket"
expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.credentials"/>
My Spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!-- This is where we configure Spring-Security -->
<security:global-method-security pre-post-annotations="enabled" />
<!-- <security:global-method-security secured-annotations="enabled" /> -->
<security:http auto-config="false" use-expressions="true" access-denied-page="/access-deniad"
entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/RetailEnterpriseSuite/login.do" access="permitAll" requires-channel="https" />
<security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" requires-channel="https"/>
<!-- <security:intercept-url pattern="/common" access="hasRole('ROLE_USER')"/> -->
<security:intercept-url pattern="/users" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/*" access="permitAll" requires-channel="any"/>
<security:logout
invalidate-session="true"
logout-success-url="/login.html"
logout-url=""/>
<!--
Querying the SessionRegistry for currently authenticated users and their sessions
http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html#list-authenticated-principals
-->
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER"/>
<security:custom-filter ref="singleEntryFilter" after="FORM_LOGIN_FILTER"/>
<security:session-management session-authentication-strategy-ref="sas"/>
</security:http>
<bean id="singleEntryFilter" class="com.stc.res.filter.SingleEntryFilter"
p:redirectURI="/login.html">
<property name="guardURI">
<list>
<!-- <value>/index.html</value> -->
<value>/index.html</value>
<!-- <value>/index.html</value>
<value>/index.html</value>
<value>/index.html</value>
<value>/index.html</value> -->
</list>
</property>
</bean>
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:sessionAuthenticationStrategy-ref="sas"
p:authenticationManager-ref="authenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler"/>
<!-- We just actually need to set the default failure url here -->
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/loginfailed" />
<!-- We just actually need to set the default target url here -->
<bean id= "customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="redirectStrategy" ref="customSuccessRedirStrategy" />
</bean>
<!-- <bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:redirectStrategy-ref="customSuccessRedirStrategy" /> -->
<bean id= "customSuccessRedirStrategy" class=" com.stc.res.customeredirection.CustomSuccessRedirection"> </bean>
<!-- The AuthenticationEntryPoint is responsible for redirecting the user to a particular page, like a login page,
whenever the server sends back a response requiring authentication -->
<!-- See Spring-Security Reference 5.4.1 for more info -->
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login.html"/>
<!-- Declare an authentication-manager to use a custom userDetailsService -->
<!-- It's important to set the alias here because it's used by the authenticationFilter -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="userservice">
<security:password-encoder ref="passwordEncoder">
<security:salt-source ref="saltSource"/>
</security:password-encoder>
</security:authentication-provider>
<security:authentication-provider user-service-ref="jdbcUserService"/>
</security:authentication-manager>
<bean id="userservice" class="com.stc.res.service.UserLoginService" >
<property name="usrlogindao" ref="userLogindao"/>
</bean>
<bean id="userLogindao" class = "com.stc.res.dao.UserLoginDao" />
<bean id="jdbcUserService" class="com.stc.res.service.JdbcUserService">
<property name="customJdbcDao" ref="custjdbcDao"/>
</bean>
<bean id="custjdbcDao" class= "com.stc.res.dao.CustomJdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcAdminUserService" class="com.stc.res.controller.JdbcAdminUserService">
<property name="dataSource" ref="dataSource"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<!-- Use a Sha encoder since the user's passwords are stored as Md5 in the database -->
<bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" id="passwordEncoder"/>
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
<property name="userPropertyToUse" value="username"/>
</bean>
<!-- <security:bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.PersistentTokenBasedRememberMeServices">
<property name="tokenRepository" ref="jdbcTokenRepository" />
<property name="userDetailsService" ref="userservice" />
<property name="key" value="springRocks" />
<property name="alwaysRemember" value="false" />
</security:bean>
Uses a database table to maintain a set of persistent login data
<security:bean id="jdbcTokenRepository" class="org.springframework.security.ui.rememberme.JdbcTokenRepositoryImpl">
<property name="createTableOnStartup" value="false" />
<property name="dataSource" ref="dataSource" />
</security:bean>
-->
<!-- An in-memory list of users. No need to access an external database layer.
See Spring Security 3.1 Reference 5.2.1 In-Memory Authentication -->
<!-- john's password is admin, while jane;s password is user -->
<!-- Filter required by concurrent session handling package
The ConcurrentSessionFilter requires two properties, sessionRegistry, which generally points to an
instance of SessionRegistryImpl, and expiredUrl, which points to the page to display when a session has expired.
See: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html#list-authenticated-principals -->
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter"
p:sessionRegistry-ref="sessionRegistry"
p:expiredUrl="/login.html" />
<!-- Defines a concrete concurrent control strategy
Checks whether the user in question should be allowed to proceed, by comparing the number of
sessions they already have active with the configured maximumSessions value. The SessionRegistry
is used as the source of data on authenticated users and session data.
See: http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/web/authentication/session/ConcurrentSessionControlStrategy.html-->
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"
p:maximumSessions="1" error-if-maximum-exceeded="true" >
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
</bean>
<!-- Maintains a registry of SessionInformation instances
See: http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/core/session/SessionRegistry.html -->
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
</beans>
and I configured the in web.xml:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<display-name>MycustomFilter</display-name>
<filter-name>MycustomFilter</filter-name>
<filter-class>com.stc.res.filter.MycustomFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MycustomFilter</filter-name>
<url-pattern>/MycustomFilter</url-pattern>
</filter-mapping>
listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
Please let me know where is the fault in this code, and please guide me. I am new to spring-security. Even user can login from different browser, without logging out.
Have you tried this snippet from the official documentation (preventing multiple logins):
<security:http ... >
....
<security:session-management>
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
</security:http>