Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Spring 3.0 Security Filters, Multiple EntryPoints, AuthenticationProvider

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 ?

like image 342
Olivier Mahieu Avatar asked Feb 12 '13 09:02

Olivier Mahieu


1 Answers

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 !

like image 107
Olivier Mahieu Avatar answered Oct 22 '22 17:10

Olivier Mahieu