Announcement Announcement Module
Collapse
No announcement yet.
LDAP Authentication creating a custom UserDetails object Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • LDAP Authentication creating a custom UserDetails object

    Hi,

    I've been doing a lot reading to try and have my Spring Web App create a custom UserDetails object during user authentication but I'm not getting anywhere so I'm hoping somebody out there can help?

    Requirements
    - Spring MVC Web App with Spring Security
    - LDAP/Active Directory Authentication
    - Custom UserDetails object created during logon
    - UserDetails object populated with details obtained from service layer (AD it not used because it does not contain any authorities/groups it is only used for authentication)

    What I have so far
    - LDAP/AD authentication working using the following:

    Code:
        <security:authentication-manager>
            <security:authentication-provider ref="ldapAuthenticationProvider" />
        </security:authentication-manager>
    
        <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
            <constructor-arg ref="authentication" />
        </bean>
    
        <bean id="authentication" class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <constructor-arg ref="contextSource" />
            <property name="userSearch" ref="userSearch"/>
        </bean>
        
        <security:ldap-server id="contextSource" url="${ldap.server.url}"
            root="${ldap.searchBase}" 
            manager-dn="${ldap.admin.user}"
            manager-password="${ldap.admin.password}" />
    
        <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
            <constructor-arg index="0" value="${ldap.filter}"/>
            <constructor-arg index="1" value="${ldap.searchId}"/>
            <constructor-arg index="2" ref="contextSource" />
    This works great and lets me login to my Spring MVC webapp with my Active Directory credentials.

    I'm now struggling to understand how I manipulate the UserDetails object which is created during Authentication. I've searched Google and read a lot of different posts on this but I'm not clear on the path I should take so would appreciate any advice on the direction to go with this. Ideally the Authentication process would be creating a custom UserDetails object and this would get populated with authorities from a service.


    Do I write a service which runs after the authentication bean has executed as follows:

    Code:
        <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
            <constructor-arg ref="authentication" />
            <constructor-arg ref="authorisation" />
        </bean>
    
        <bean id="authorisation" class="com.app.path.security.AuthoritiesPopulator" />
    This then implements a custom UserDetailsService and UserDetails class, but this seems to be creating another UserDetails object after one has already been created during Authentication, is that right?


    What I want to end up with is a custom UserDetails object in the session that has been populated from details retrieved in a service layer and this will include roles, permissions, projects the user has access to etc..

    Hopefully this makes sense let me know if the question isn't clear.

    Thanks for your help!

  • #2
    +1

    I need the same answer. I have a working test application which authenticates against Active Directory via LDAP (over TLS), and while I've created various user-type objects, they each apply to the application scope rather than session scope. I've worked around this via a hack (by creating a Hashtable and storing my custom user details in it), but this is obviously not ideal.

    Since the authentication component I've customized (ActiveDirectoryLdapAuthenticationProvider) takes place before the Authentication object has been created, I have no access to the session at precisely the time I need access. I gather that the UserDetails object is built outside the session (or before the session ID is generated) but with session scope, but as lindo has noted, I see no way to pair my authentication requirements with my custom user details requirements.

    Here are the relevant portions of 'security.xml' and 'ldap.xml', respectively:

    Code:
    	<security:authentication-manager>
    		<security:authentication-provider ref="ldapAuthenticationProvider"/>
    	</security:authentication-manager>
    Code:
    	<bean id="ldapAuthenticationProvider"
    				class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider">
    		<!-- this class overloads methods in a hacked (read: non-final) version of ActiveDirectoryLdapAuthenticationProvider -->
    			
    			<constructor-arg value="test.server"/>
    			<constructor-arg value="ldap://192.168.0.3:389"/>
    			<property name="convertSubErrorCodesToExceptions" value="true"/>
    	</bean>
    I, too, would appreciate any assistance.

    Comment


    • #3
      @Lindo
      When you call SecurityContextHolder.getContext().getAuthenticati on (), you get a class implementing the Authentication interface, which may be a class that you created or a build-in one. The actual authentication is done by the ProviderManager.authenticate() method, in your case that would be the LdapAuthenticationProvider class.

      Our application contains multiple sections/projects and user may have permission in one project but not in others, so we can't use the standard Spring role based (annotation style) permissions. We have to resolve the LDAP groups to permission in a specific projects base on the LDAP groups and role mapping in each project, this project specific information is stored in our own custom Authentication class. After the user is authentication by LDAP, we grap the ldap groups and translates that information into the project specific permissions - this is done inside our own custom ProviderManager implementation, similar to what Stan outlines.

      The CustomProviderManager class is constructed with a reference to the LdapAuthenticationProvider, and inside its authenticate() method, it just delegates to LDAP, and then use the Authentication returned by LDAP to create and return a CustomAuthentication.

      Comment


      • #4
        @Stan
        You can only access the session and application scope inside authentication chain when you are in the filters, not inside the ProviderManager. However since all authentication classes are instantiated by Spring, you can just autowire, or constructor inject any Spring bean you need. I would argue that Spring effectively replaces the application scope as the default mechanism for getting application wide singletons.

        Comment


        • #5
          Thanks for your replies.

          What I ended up doing is changing the "ldapAuthenticationProvider" bean and leaving the rest of my config the same.

          Code:
              <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
                  <constructor-arg ref="authentication" />
                  <property name="userDetailsContextMapper" ref="CustomUserDetailsContextMapper" />
              </bean>
          
               <bean id="CustomUserDetailsContextMapper" class="com.my.app.path.CustomUserDetailsContextMapper" />
          In the CustomUserDetailsContextMapper I create and populate the UserDetails object how I want it then return it.

          It seems to work great, however I'm not sure if this is the correct way to do this. Please comment if there is a better solution out there?

          Comment


          • #6
            Originally posted by lindo View Post
            Thanks for your replies.

            What I ended up doing is changing the "ldapAuthenticationProvider" bean and leaving the rest of my config the same.

            Code:
                <bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
                    <constructor-arg ref="authentication" />
                    <property name="userDetailsContextMapper" ref="CustomUserDetailsContextMapper" />
                </bean>
            
                 <bean id="CustomUserDetailsContextMapper" class="com.my.app.path.CustomUserDetailsContextMapper" />
            In the CustomUserDetailsContextMapper I create and populate the UserDetails object how I want it then return it.

            It seems to work great, however I'm not sure if this is the correct way to do this. Please comment if there is a better solution out there?
            can you please post your code for CustomUserDetailsContextMapper

            Comment

            Working...
            X