Announcement Announcement Module
Collapse
No announcement yet.
Spring Flex Saml2 combined webapp, SessionFixationProtectionStrategy help Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Flex Saml2 combined webapp, SessionFixationProtectionStrategy help

    We have two separate web apps, both are secured using spring security, and both work fine on their own.

    The first webapp is a spring-flex-blazeds webapp, using spring-flex-core-1.5.2.release, spring-security-core-3.1.0.release, spring-core-3.1.1.release, and which has the following basic security setup:

    <security:http auto-config="true">
    <!-- All security is done at the method level by annotations -->
    <security:anonymous/>
    <security:intercept-url pattern="/**"/>
    <security:form-login login-page="/index.jsp"/>
    </security:http>

    The second webapp is the spring-saml2 example webapp, right out of the box from Cloudseal's patched version of the saml2-core project ( http://www.cloudseal.com/java-downloads/ ), which has, among other configurations, this basic security setup:

    <security:http entry-point-ref="samlEntryPoint">
    <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>

    What we are trying to do is integrate the saml2 example into our current webapp, so as to implement sso capabilities in our spring-flex-blazeds web app. For example, if a user hits our normal login page at /index.jsp, then they have to login as normal; but if they hit, say /saml/index.jsp, then the sso-saml2 filter would initiate the saml2 login flow.

    When I try to combine the web apps, by integrating the code from the saml2 example into my web app, and then trying to combine the security-context.xml files, I get the following root error:

    _loginCommandPostProcessor expected single matching bean but found 2: [org.springframework.security.web.authentication.se ssion.SessionFixationProtectionStrategy#0, org.springframework.security.web.authentication.se ssion.SessionFixationProtectionStrategy#1]

    The _loginCommandPostProcessor is a spring SecurityConfigurationPostProcessor instance, and seems to be coming from the spring-flex-core jar.

    Spring is trying to Autowire the SessionFixationProtectionStrategy instance implementing SessionAuthenticationStrategy into the _loginCommandPostProcessor.

    Can someone explain what is causing spring to instantiate two SessionFixationProtectionStrategy instances, which causes the error, and how can I fix the configuration so that spring only creates one?

    Does anyone know of an example security config that combines the spring-flex with saml2?

  • #2
    The SessionFixationProtectionStrategy is created and wired up by the Spring Security namespace parser. It would be helpful if you could post the final merged security context file that is giving you the problems so we can take a look.

    Comment


    • #3
      Here is the final merged security-context.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/schem...-beans-3.0.xsd
      http://www.springframework.org/schema/security http://www.springframework.org/schem...curity-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

      <!-- Enable autowiring -->
      <context:annotation-config/>
      <context:component-scan base-package="org.springframework.security.saml.web" />

      <security:http entry-point-ref="samlEntryPoint" pattern="/saml/web" security="none" />
      <security:http entry-point-ref="samlEntryPoint" pattern="/saml/logout.jsp" security="none" />
      <security:http entry-point-ref="samlEntryPoint" pattern="/saml/login.jsp" security="none" />
      <security:http entry-point-ref="samlEntryPoint" pattern="/saml/favicon.ico" security="none" />

      <security:http entry-point-ref="samlEntryPoint" pattern="/saml/*">
      <security:intercept-url pattern="/saml/**" access="IS_AUTHENTICATED_FULLY"/>
      <security:custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
      <security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
      </security:http>

      <!-- All security is done at the method level by annotations for the root pattern -->
      <security:http auto-config="true">
      <security:anonymous/>
      <security:intercept-url pattern="/**"/>
      <security:form-login login-page="/index.jsp"/>
      </security:http>

      <security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled"/>

      <bean id="preAuthenticatedEntryPoint"
      class="org.springframework.security.web.authentica tion.Http403ForbiddenEntryPoint"/>

      <security:authentication-manager>
      <security:authentication-provider>
      <security:user-service>
      <security:user name="anon" password="5f4dcd3b5aa765a61d8327deb8822f99" authorities="ROLE_ANONYMOUS" />
      </security:user-service>
      </security:authentication-provider>
      </security:authentication-manager>

      <bean id="samlFilter" class="org.springframework.security.web.FilterChai nProxy">
      <security:filter-chain-map path-type="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.authentica tion.SavedRequestAwareAuthenticationSuccessHandler ">
      <property name="defaultTargetUrl" value="/"/>
      </bean>

      <!-- Handler for successful logout -->
      <bean id="successLogoutHandler" class="org.springframework.security.web.authentica tion.logout.SimpleUrlLogoutSuccessHandler">
      <property name="defaultTargetUrl" value="/logout.jsp"/>
      </bean>

      <!-- Register authentication manager with SAML provider -->
      <!-- Note the erase-credentials=false attribute. This is required if you want to use the
      sample app as is because index.jsp looks for the SAMLCredential object. Spring security
      now deletes the credentials by default which makes sense for standard username/password
      authentication, but it's not required for SAML -->
      <security:authentication-manager alias="authenticationManager" erase-credentials="false">
      <security:authentication-provider ref="samlAuthenticationProvider" />
      </security:authentication-manager>

      <!-- Logger for SAML messages and events -->
      <bean id="samlLogger" class="org.springframework.security.saml.log.SAMLD efaultLogger"/>

      <!-- Central storage of cryptographic keys -->
      <bean id="keyManager" class="org.springframework.security.saml.key.JKSKe yManager">
      <constructor-arg value="classpath:security/keystore.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.SAMLEntry Point">
      <property name="defaultProfileOptions">
      <bean class="org.springframework.security.saml.websso.We bSSOProfileOptions">
      <property name="includeScoping" value="false"/>
      </bean>
      </property>
      </bean>

      <!-- IDP Discovery Service -->
      <bean id="samlIDPDiscovery" class="org.springframework.security.saml.SAMLDisco very">
      <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">
      <!-- by default spring-saml will ask the IDP to use artifact binding for it's response
      (which is first in the list of assertionConsumerIndex) we want to use post binding
      which is second in the list. This approach seems a bit flaky so it's much better
      to use the cloudseal saml namespace when we've patched it for spring security 3.1 -->
      <property name="assertionConsumerIndex" value="1" />
      </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 configuration - paths to metadata of IDPs in circle of trust is here -->
      <!-- Do no forget to call iniitalize method on providers -->
      <bean id="metadata" class="org.springframework.security.saml.metadata. CachingMetadataManager">
      <constructor-arg>
      <list>
      <!--
      <bean class="org.springframework.security.saml.metadata. ExtendedMetadataDelegate">
      <constructor-arg>
      <bean class="org.opensaml.saml2.metadata.provider.Filesy stemMetadataProvider">
      <constructor-arg>
      <value type="java.io.File">classpath:security/idp.xml</value>
      </constructor-arg>
      <property name="parserPool" ref="parserPool"/>
      </bean>
      </constructor-arg>
      <constructor-arg>
      <bean class="org.springframework.security.saml.metadata. ExtendedMetadata">
      </bean>
      </constructor-arg>
      </bean>
      -->
      <bean class="org.opensaml.saml2.metadata.provider.HTTPMe tadataProvider">
      <!-- URL containing the metadata -->
      <constructor-arg>
      <!-- <value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value> -->
      <value type="java.lang.String">http://vail:8080/openam_954/saml2/jsp/exportmetadata.jsp</value>
      </constructor-arg>
      <!-- Timeout for metadata loading in ms -->
      <constructor-arg>
      <value type="int">5000</value>
      </constructor-arg>
      <property name="parserPool" ref="parserPool"/>
      </bean>
      </list>
      </constructor-arg>
      <!-- OPTIONAL property: can tell the system which IDP should be used for authenticating user by default. -->
      <!-- <property name="defaultIDP" value="http://cloudseal.com"/> -->
      </bean>

      <!-- SAML Authentication Provider responsible for validating of received SAML messages -->
      <bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthe nticationProvider">
      <!-- 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.S AMLContextProviderImpl"/>

      ... (remaining beans are the same as the saml2 sample)...

      </beans>

      Comment


      • #4
        Also, here is my web.xml:

        <?xml version="1.0" encoding="UTF-8"?>
        <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

        <display-name>Flex Web Application</display-name>

        <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        /WEB-INF/spring/*-context.xml
        </param-value>
        </context-param>

        <error-page>
        <error-code>404</error-code>
        <location>/htmlErrors/404.html</location>
        </error-page>

        <!-- spring specific filter -->
        <filter>
        <filter-name>openSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.O penSessionInViewFilter</filter-class>
        <init-param>
        <param-name>flushMode</param-name>
        <param-value>ALWAYS</param-value>
        </init-param>
        </filter>
        <filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>/messagebroker/*</url-pattern>
        </filter-mapping>

        <!-- NOTE: the order in which the listeners are listed here is VERY important. The spring context
        loader needs to be listed first, and therefore is called last when the web context
        is destroyed. it needs to be called last because we might hit the db inside the
        WebAppContextListener class, and we don't want spring to close the db connection pool
        beforehand. -->
        <listener>
        <listener-class>org.springframework.web.context.ContextLoade rListener</listener-class>
        </listener>
        <listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
        </listener>
        <listener>
        <description>sessionListener</description>
        <listener-class>com.digabit.web.servlet.listener.WebAppSessi onListener</listener-class>
        </listener>
        <listener>
        <description>contextListener</description>
        <listener-class>com.digabit.web.servlet.listener.WebAppConte xtListener</listener-class>
        </listener>

        <servlet>
        <servlet-name>FlexWebApp</servlet-name>
        <servlet-class>com.digabit.web.servlet.DigabitDispatcherSer vlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        </servlet>

        <servlet-mapping>
        <servlet-name>FlexWebApp</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
        </servlet-mapping>

        <resource-ref>
        <description>mail</description>
        <res-ref-name>mail/Session</res-ref-name>
        <res-type>javax.mail.Session</res-type>
        <res-auth>Container</res-auth>
        </resource-ref>

        <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>

        <!-- Saml stuff -->
        <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFil terProxy</filter-class>
        </filter>
        <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/saml/*</url-pattern>
        </filter-mapping>

        <!-- Spring Security -->
        <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFil terProxy</filter-class>
        </filter>
        <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        </filter-mapping>

        </web-app>

        Comment


        • #5
          I suspect that you are seeing duplicate SessionFixationProtectionStrategy beans because you have two http elements (ignoring those with security="none"). Normally this would not be a problem because the SessionFixationProtectionStrategy is an anonymous bean that is injected into the parent filter. This is what the namespace effectively does:

          Code:
          <bean id="formLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
              <property name="authenticationManager" ref="authenticationManager" />
              <property name="authenticationSuccessHandler">
                  <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
                      <property name="defaultTargetUrl" value="/index.jsp" />
                  </bean>
              </property>
              <property name="sessionAuthenticationStrategy">
                  <bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
              </property>
          </bean>
          However in your case you have a loginCommandPostProcessor which uses autowiring and as a consequence it falls over when it finds two beans of type SessionFixationProtectionStrategy.

          I would suggest that you merge the two http elements i.e. something like:

          Code:
          <security:http entry-point-ref="samlEntryPoint" auto-config="true">
              <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:anonymous/>
              <security:intercept-url pattern="/**"/>
              <security:form-login login-page="/index.jsp"/>
          </security:http>
          Alternatively you could try creating the SessionFixationProtectionStrategy yourself and passing it into both http elements:

          Code:
          <beans:bean id="sas"
                class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/>
          
          <security:http entry-point-ref="samlEntryPoint">
              <session-management session-authentication-strategy-ref="sas"/>
              ...
          </security:http>
          Or you could disable session fixation protection completely:

          Code:
          <security:http>
              <session-management session-fixation-protection="false" />
              ...
          </security:http>

          Comment


          • #6
            Merging the two http elements worked, in that I get past the duplicate SessionFixationProtectionStrategy error; but now there is another conflict:

            The spring-flex loginCommandPostProcessor ( a SecurityConfigurationPostProcessor instance ) also has an autowired FilterChainProxy, which conflicts with the samlFilter, resulting in this duplicate bean definition error:

            10:43:47,273 ERROR org.springframework.web.context.ContextLoader - Context initialization failed
            org.springframework.beans.factory.UnsatisfiedDepen dencyException: Error creating bean with name '_loginCommandPostProcessor': Unsatisfied dependency expressed
            hrough bean property 'filterChainProxy': : No unique bean of type [org.springframework.security.web.FilterChainProxy] is defined: expected single matching bean
            but found 2: [org.springframework.security.filterChainProxy, samlFilter];

            Any thoughts on how to get around that one?

            Comment


            • #7
              Solved.

              To get around the spring-flex FilterChainProxy conflict, I just needed to define my own bean, specifically with id="_loginCommandPostProcessor", and manually set the filterChainProxy property, like so:

              <bean id="defaultFilter" class="org.springframework.security.web.FilterChai nProxy"/>

              <bean id="_loginCommandPostProcessor" class="org.springframework.flex.security3.Security ConfigurationPostProcessor">
              <property name="filterChainProxy" ref="defaultFilter"/>
              </bean>

              With this, I can now have multiple beans of type FilterChainProxy defined, thus combining the spring-flex security namespace definition, with other spring security namespace definitions.

              <flex:message-broker>
              <flex:secured />
              </flex:message-broker>

              Comment


              • #8
                I'm glad you got it working

                The introduction of auto wiring along with namespaces has greatly simplified things in the spring world, but sometimes it bites you because you don't know what's happening under the covers

                Comment

                Working...
                X