Announcement Announcement Module
Collapse
No announcement yet.
Combine pre-authentication with LDAP for user details and authorities Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Combine pre-authentication with LDAP for user details and authorities

    I was wondering if I can combine pre-authentication with LDAP for user details and authorities. I assume yes because Spring Security is prepared to handle a myriad of security infrastructures.

    Our setup is IIS -> Tomat -> Spring Security (SS). Hence, once the request reaches SS it has its remoteUser and principal set - SS considers it pre-authenticated. However, I need an LDAP lookup (bind with a system account) for
    • user details attributes
    • group memberships (Active Directory) transformed to SS authorities

    Pre-authentication is pretty straight-forward.
    Code:
    ...
        <security:custom-filter position="PRE_AUTH_FILTER" ref="preAutenticatedProcessingFilter" />
      </security:http>
    
      <!-- Security Filter for J2EE (ie. getPrincipal) -->
      <bean id="preAutenticatedProcessingFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
        <!-- property is not used in class but in AbstractPreAuthenticatedProcessingFilter super class -->
        <property name="authenticationManager" ref="authenticationManager" />
      </bean>
    ...
    However, I'm not quite sure how to configure LDAP - based on http://static.springsource.org/sprin...ence/ldap.html - because I need no password comparison or the like as the user is pre-authenticated.
    Last edited by marcelstoer; Oct 14th, 2011, 02:28 PM.

  • #2
    There may be several options to achieve this - if you find a better one than the one outlined below please let me know.

    HTML Code:
    ...
        <security:custom-filter position="PRE_AUTH_FILTER" ref="preAutenticatedProcessingFilter" />
      </security:http>
    
      <!-- Security Filter for J2EE (ie. getPrincipal) -->
      <bean id="preAutenticatedProcessingFilter" class="WindowsPreAuthenticationProcessingFilter">
        <!-- property is not used in class but in AbstractPreAuthenticatedProcessingFilter super class -->
        <property name="authenticationManager" ref="authenticationManager" />
      </bean>
    
      <security:authentication-manager alias="authenticationManager">
        <!-- won't work because this instantiates a DaoAuthenticationProvider which doesn't know how to deal with a PreAuthenticatedAuthenticationToken -->
        <!-- <security:authentication-provider user-service-ref="ldapUserService"/> -->
    
        <!-- won't work because this instantiates a LdapAuthenticationProvider which doesn't know how to deal with a PreAuthenticatedAuthenticationToken -->
        <!-- <security:ldap-authentication-provider server-ref="ldapServer" user-search-filter="(uid={0})" group-search-base="OU=Users,OU=Windows7,OU=NCA"/> -->
    
        <security:authentication-provider ref="preAuthenticatedLdapAuthenticationProvider" />
      </security:authentication-manager>
    
      <bean id="preAuthenticatedLdapAuthenticationProvider" class="PreAuthenticatedLdapAuthenticationProvider">
        <property name="userDetailsService" ref="ldapUserService" />
      </bean>
    
      <security:ldap-server id="ldapServer" url="ldap://<servername>:<port>/<base_DC>"
        manager-dn="<full_DN_not_relative_to_base_DC>" manager-password="<password>" />
    
      <bean id="ldapUserService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
        <constructor-arg ref="ldapUserSearch"/>
        <constructor-arg ref="ldapAuthoritiesPopulator"/>
      </bean>
      <bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
        <constructor-arg value="OU=Users,OU=Windows7,OU=NCA"/> <!-- user search base, RELATIVE TO SERVER CONTEXT! -->
        <constructor-arg value="(uid={0})"/> <!-- user search filter -->
        <constructor-arg ref="ldapServer"/>
      </bean>
      <bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <constructor-arg ref="ldapServer" />
        <constructor-arg value="CN=Users" /> <!-- group search base, RELATIVE TO SERVER CONTEXT! -->
        <property name="groupRoleAttribute" value="cn" />
        <property name="defaultRole" value="ROLE_USER" />
      </bean>
    ...
    There are only two custom pieces in this setup: WindowsPreAuthenticationProcessingFilter and PreAuthenticatedLdapAuthenticationProvider. The first isn't even really necessary.

    When the authenticated request is passed from IIS to Tomcat remoteUser and principal#name in the request are equal. The value is "<Windows_domain>\username". WindowsPreAuthenticationProcessingFilter simply strips the "<Windows_domain>\" segment to allow subsequent classes further down the control flow to deal with the username only:

    Code:
    public class WindowsPreAuthenticationProcessingFilter extends J2eePreAuthenticatedProcessingFilter {
      @Override
      protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
        Object principalName = super.getPreAuthenticatedPrincipal(httpRequest);
        if (principalName != null) {
          principalName = StringUtils.substringAfterLast((String) principalName, "\\");
        }
        return principalName; 
      }
    }
    Then it gets a little more tricky. When you use Spring Security pre-authentication the auth token object (i.e. the concrete o.s.s.core.Authentication implementation) is a PreAuthenticatedAuthenticationToken. However, most o.s.s.authentication.AuthenticationProvider implementations don't know how to deal with this. Their respective supports() method declares that they can't handle PreAuthenticatedAuthenticationTokens.

    The first obvious approach (for me at least) would be to extend/modify the standard LdapAuthenticationProvider. However, since my request is pre-authenticated I don't need any LDAP authentication (neither bind nor password authentication). I would either have had to
    a) extend LdapAuthenticationProvider overriding authenticate() -> copy/paste from original method but leave out authenticator stuff
    b) use LdapAuthenticationProvider as it is but providing my own LdapAuthenticator implementation that only reads the use from LDAP (no authentication)

    Neither of this really appealed to me as I had the nagging feeling that there must be a more "direct" way. Peter Mularien's excellent Spring Security book pointed me in the right direction when I learned that there's an LdapUserDetailsService that does all the good things the LdapAuthenticationProvider does - except for authentication. Bingo! All there was left to do was hook this service into a regular DaoAuthenticationProvider, but one that can deal with PreAuthenticatedAuthenticationTokens. Enter PreAuthenticatedLdapAuthenticationProvider:

    Code:
    public class PreAuthenticatedLdapAuthenticationProvider extends DaoAuthenticationProvider {
    
      public boolean supports(Class<? extends Object> authentication) {
        return (PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication));
      }
    
      @Override
      protected void additionalAuthenticationChecks(UserDetails userDetails,
          UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        // no salt or password check -> user is pre-authenticated!
      }
    
      @Override
      public Authentication authenticate(final Authentication authentication)
          throws AuthenticationException {
        final Authentication usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
            authentication.getPrincipal(), authentication.getPrincipal());
        return super.authenticate(usernamePasswordAuthenticationToken);
      }
    }

    Comment

    Working...
    X