6

I need my security to have the following logic :

  1. Check the presence of a header parameter
  2. Depending on the presence of the paremeter do either a redirect to a login page (if not authenticated) , or check basic authentication token

In both cases, I have the same authentication provider , but I can't have it working. The delegating entrypoint works fine, but I never get into my custom authenticationprovider ...

Here is my security config :

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

<security:http entry-point-ref="delegatingAuthenticationEntryPoint"
    use-expressions="true" auto-config="false">
    <!-- <security:custom-filter position="FORM_LOGIN_FILTER" -->
    <!-- ref="usernamePasswordAuthenticationFilter" /> -->
    <!-- <security:custom-filter position="BASIC_AUTH_FILTER" -->
    <!-- ref="basicAuthenticationFilter" /> -->
    <security:intercept-url pattern="/login*"
        filters="none" />
    <security:intercept-url pattern="/portimaLogin*"
        filters="none" />
    <security:intercept-url pattern="/**"
        access="isAuthenticated()" />
</security:http>

<bean id="delegatingAuthenticationEntryPoint"
    class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
    <constructor-arg>
        <map>
            <entry key="hasHeader('portima','true')" value-ref="PortimaLoginUrlAuthenticationEntryPoint" />
        </map>
    </constructor-arg>
    <property name="defaultEntryPoint" ref="authenticationEntryPoint" />
</bean>

<bean id="usernamePasswordAuthenticationFilter"
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</bean>

<bean id="basicAuthenticationFilter"
    class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>

<bean id="PortimaLoginUrlAuthenticationEntryPoint"
    class="be.ap.common.security.spring.PortimaLoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="${portima.login.page}" />
</bean>

<bean id="authenticationEntryPoint"
    class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
    <property name="realmName" value="AP" />
</bean>

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider
        ref="authenticationProvider" />
</security:authentication-manager>

<bean id="authenticationProvider" class="be.ap.common.security.spring.APAuthenticationProvider" />

<bean id="userDetailsService" class="be.ap.common.security.spring.APUserDetailsService" />

Any idea ?

Olivier Mahieu
  • 381
  • 1
  • 2
  • 11
  • Does your custom AuthenticationProvider implement the `supports()` method correctly? If it returns false for the `UsernamePasswordAuthenticationToken` class, then it will never be asked to process the authentication tokens created by your filters. – zagyi Feb 12 '13 at 10:31
  • It doesn't even go in the support method... – Olivier Mahieu Feb 12 '13 at 10:47
  • @Override public boolean supports(Class extends Object> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } – Olivier Mahieu Feb 12 '13 at 10:49
  • It seems then that your declared filters are actually ignored. Refer to the manual (http://static.springsource.org/spring-security/site/docs/3.1.x/reference/ns-config.html#ns-custom-filters) to see how filters created by the namespace config can be replaced. – zagyi Feb 12 '13 at 12:01

1 Answers1

12

I finally have it to work.

Here is my context file :

    <security:http entry-point-ref="delegatingAuthenticationEntryPoint"
    use-expressions="true">
    <security:custom-filter position="PRE_AUTH_FILTER"
        ref="preAuthenticationFilter" />
    <security:custom-filter position="FORM_LOGIN_FILTER"
        ref="usernamePasswordAuthenticationFilter" />
    <security:custom-filter position="BASIC_AUTH_FILTER"
        ref="basicAuthenticationFilter" />
    <security:intercept-url pattern="/login*"
        filters="none" />
    <security:intercept-url pattern="/portimaLogin*"
        filters="none" />
    <security:intercept-url pattern="/accessDenied*"
        filters="none" />
    <security:intercept-url pattern="/**"
        access="isAuthenticated()" />
    <security:access-denied-handler ref="accessDeniedHandler" />
</security:http>

<!-- Spring Security Custom Filters -->

<bean id="usernamePasswordAuthenticationFilter"
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationFailureHandler" ref="authenticationFailureHandler" />
</bean>

<bean id="basicAuthenticationFilter"
    class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
</bean>

<bean id="preAuthenticationFilter" class="be.ap.common.security.spring.APPreAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
</bean>

<!-- Spring Security Custom EntryPoint -->

<bean id="delegatingAuthenticationEntryPoint"
    class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
    <constructor-arg>
        <map>
            <entry key="hasHeader('portima','true')" value-ref="PortimaLoginUrlAuthenticationEntryPoint" />
        </map>
    </constructor-arg>
    <property name="defaultEntryPoint" ref="authenticationEntryPoint" />
</bean>

<bean id="PortimaLoginUrlAuthenticationEntryPoint"
    class="be.ap.common.security.spring.PortimaLoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="${portima.login.page}" />
</bean>

<bean id="authenticationEntryPoint"
    class="be.ap.common.security.spring.APBasicAuthenticationEntryPoint">
    <property name="realmName" value="AP" />
</bean>
<bean id="accessDeniedHandler"
    class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
    <property name="errorPage" value="/accessDenied" />
</bean>

<bean id="authenticationFailureHandler"
    class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
    <property name="exceptionMappings">
        <props>
            <prop
                key="org.springframework.security.authentication.BadCredentialsException">
                /accessDenied
            </prop>
            <prop
                key="org.springframework.security.authentication.CredentialsExpiredException">
                /accessDenied
            </prop>
            <prop key="org.springframework.security.authentication.LockedException">
                /accessDenied
            </prop>
            <prop
                key="org.springframework.security.authentication.DisabledException">
                /accessDenied
            </prop>
        </props>
    </property>
</bean>

<!-- Spring Security Authentication Manager -->

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider
        ref="authenticationProvider" />
</security:authentication-manager>

<bean id="authenticationProvider" class="be.ap.common.security.spring.APAuthenticationProvider" />

<bean id="userDetailsService" class="be.ap.common.security.spring.APUserDetailsService" />

<!-- for Mock -->
<bean id="SSOService" class="be.ap.security.service.SSOServiceMockImpl" />

As you can see I added a few things too.

To fix it I remorve the auto-config attribute, uncommented the filters, and defined them properly.

For others who wants a quick understanding of what it does , here is the flow :

  1. PRE_AUTH_FILTER will check a SSO like service to prefill the Authentication object (if already authenticated in SSO)
  2. delegatingAuthenticationEntryPoint will then choose how to authenticate depending on the request header
  3. The two ways are :
    • custom LoginUrlAuthenticationEntryPoint
    • custom BasicAuthenticationEntryPoint

BasicAuth and LoginURLAuth use the same AuthenticationProvider when PreAuth uses my SSO service.

Hope it helps someone else !

Olivier Mahieu
  • 381
  • 1
  • 2
  • 11