123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="ldap">
- <info>
- <title>LDAP Authentication</title>
- </info>
- <section xml:id="ldap-overview">
- <info>
- <title>Overview</title>
- </info>
- <para>LDAP is often used by organizations as a central repository for user information and
- as an authentication service. It can also be used to store the role information for
- application users.</para>
- <para>There are many different scenarios for how an LDAP server may be configured so Spring
- Security's LDAP provider is fully configurable. It uses separate strategy interfaces for
- authentication and role retrieval and provides default implementations which can be
- configured to handle a wide range of situations.</para>
- <para>You should be familiar with LDAP before trying to use it with Spring Security. The
- following link provides a good introduction to the concepts involved and a guide to
- setting up a directory using the free LDAP server OpenLDAP: <uri
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xlink:href="http://www.zytrax.com/books/ldap/"
- >http://www.zytrax.com/books/ldap/</uri>. Some familiarity with the JNDI APIs used
- to access LDAP from Java may also be useful. We don't use any third-party LDAP libraries
- (Mozilla, JLDAP etc.) in the LDAP provider, but extensive use is made of Spring LDAP, so
- some familiarity with that project may be useful if you plan on adding your own
- customizations.</para>
- </section>
- <section>
- <info>
- <title>Using LDAP with Spring Security</title>
- </info>
- <para> LDAP authentication in Spring Security can be roughly divided into the following
- stages. <orderedlist inheritnum="ignore" continuation="restarts">
- <listitem>
- <para>Obtaining the unique LDAP <quote>Distinguished Name</quote>, or DN, from
- the login name. This will often mean performing a search in the directory,
- unless the exact mapping of usernames to DNs is known in advance.</para>
- </listitem>
- <listitem>
- <para>Authenticating the user, either by binding as that user or by performing a
- remote <quote>compare</quote> operation of the user's password against the
- password attribute in the directory entry for the DN.</para>
- </listitem>
- <listitem>
- <para>Loading the list of authorities for the user.</para>
- </listitem>
- </orderedlist> The exception is when the LDAP directory is just being used to retrieve
- user information and authenticate against it locally. This may not be possible as
- directories are often set up with limited read access for attributes such as user
- passwords. </para>
- <para> We will look at some configuration scenarios below. For full information on available
- configuration options, please consult the security namespace schema (information from
- which should be available in your XML editor). </para>
- </section>
- <section xml:id="ldap-server">
- <info>
- <title>Configuring an LDAP Server</title>
- </info>
- <para> The first thing you need to do is configure the server against which authentication
- should take place. This is done using the <literal><ldap-server></literal> element
- from the security namespace. This can be configured to point at an external LDAP server,
- using the <literal>url</literal> attribute: <programlisting><![CDATA[
- <ldap-server url="ldap://springframework.org:389/dc=springframework,dc=org" />
- ]]>
- </programlisting></para>
- <section>
- <info>
- <title>Using an Embedded Test Server</title>
- </info>
- <para> The <literal><ldap-server></literal> element can also be used to create an
- embedded server, which can be very useful for testing and demonstrations. In this
- case you use it without the <literal>url</literal> attribute: <programlisting><![CDATA[
- <ldap-server root="dc=springframework,dc=org"/>
- ]]>
- </programlisting> Here we've specified that the root DIT of the directory should be
- <quote>dc=springframework,dc=org</quote>, which is the default. Used this way,
- the namespace parser will create an embedded Apache Directory server and scan the
- classpath for any LDIF files, which it will attempt to load into the server. You can
- customize this behaviour using the <literal>ldif</literal> attribute, which defines
- an LDIF resource to be loaded: <programlisting><![CDATA[
- <ldap-server ldif="classpath:users.ldif" />
- ]]></programlisting> This makes it a lot easier to get up and running with LDAP, since it
- can be inconvenient to work all the time with an external server. It also insulates
- the user from the complex bean configuration needed to wire up an Apache Directory
- server. Using plain Spring Beans the configuration would be much more cluttered. You
- must have the necessary Apache Directory dependency jars available for your
- application to use. These can be obtained from the LDAP sample application. </para>
- </section>
- <section>
- <info>
- <title>Using Bind Authentication</title>
- </info>
- <para> This is the most common LDAP authentication scenario. <programlisting><![CDATA[
- <ldap-authentication-provider user-dn-pattern="uid={0},ou=people"/>
- ]]></programlisting> This simple example would obtain the DN for the user by
- substituting the user login name in the supplied pattern and attempting to bind as
- that user with the login password. This is OK if all your users are stored under a
- single node in the directory. If instead you wished to configure an LDAP search
- filter to locate the user, you could use the following: <programlisting><![CDATA[
- <ldap-authentication-provider user-search-filter="(uid={0})"
- user-search-base="ou=people"/>
- ]]></programlisting> If used with the server definition above, this would
- perform a search under the DN <literal>ou=people,dc=springframework,dc=org</literal>
- using the value of the <literal>user-search-filter</literal> attribute as a filter.
- Again the user login name is substituted for the parameter in the filter name. If
- <literal>user-search-base</literal> isn't supplied, the search will be performed
- from the root. </para>
- </section>
- <section>
- <info>
- <title>Loading Authorities</title>
- </info>
- <para> How authorities are loaded from groups in the LDAP directory is controlled by the
- following attributes. <itemizedlist>
- <listitem>
- <para>
- <literal>group-search-base</literal>. Defines the part of the directory
- tree under which group searches should be performed.</para>
- </listitem>
- <listitem>
- <para>
- <literal>group-role-attribute</literal>. The attribute which contains
- the name of the authority defined by the group entry. Defaults to
- <literal>cn</literal>
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>group-search-filter</literal>. The filter which is used to
- search for group membership. The default is
- <literal>uniqueMember={0}</literal>, corresponding to the
- <literal>groupOfUniqueMembers</literal> LDAP class. In this case,
- the substituted parameter is the full distinguished name of the user.
- The parameter <literal>{1}</literal> can be used if you want to filter
- on the login name.</para>
- </listitem>
- </itemizedlist> So if we used the following configuration <programlisting><![CDATA[
- <ldap-authentication-provider user-dn-pattern="uid={0},ou=people"
- group-search-base="ou=groups" />
- ]]></programlisting> and authenticated successfully as user <quote>ben</quote>, the subsequent
- loading of authorities would perform a search under the directory entry
- <literal>ou=groups,dc=springframework,dc=org</literal>, looking for entries
- which contain the attribute <literal>uniqueMember</literal> with value
- <literal>uid=ben,ou=people,dc=springframework,dc=org</literal>. By default the
- authority names will have the prefix <literal>ROLE_</literal> prepended. You can
- change this using the <literal>role-prefix</literal> attribute. If you don't want
- any prefix, use <literal>role-prefix="none"</literal>. For more information on
- loading authorities, see the Javadoc for the
- <classname>DefaultLdapAuthoritiesPopulator</classname> class. </para>
- </section>
- </section>
- <section>
- <info>
- <title>Implementation Classes</title>
- </info>
- <para>The namespace configuration options we've used above are simple to use and much more
- concise than using Spring beans explicitly. There are situations when you may need to
- know how to configure Spring Security LDAP directly in your application context. You may
- wish to customize the behaviour of some of the classes, for example. If you're happy
- using namespace configuration then you can skip this section and the next one. </para>
- <para> The main LDAP provider class, <classname>LdapAuthenticationProvider</classname>,
- doesn't actually do much itself but delegates the work to two other beans, an
- <interfacename>LdapAuthenticator</interfacename> and an
- <interfacename>LdapAuthoritiesPopulator</interfacename> which are responsible for
- authenticating the user and retrieving the user's set of
- <interfacename>GrantedAuthority</interfacename>s respectively.</para>
- <section xml:id="ldap-ldap-authenticators">
- <info>
- <title>LdapAuthenticator Implementations</title>
- </info>
- <para>The authenticator is also responsible for retrieving any required user attributes.
- This is because the permissions on the attributes may depend on the type of
- authentication being used. For example, if binding as the user, it may be necessary
- to read them with the user's own permissions.</para>
- <para>There are currently two authentication strategies supplied with Spring Security: <itemizedlist>
- <listitem>
- <para>Authentication directly to the LDAP server ("bind"
- authentication).</para>
- </listitem>
- <listitem>
- <para>Password comparison, where the password supplied by the user is
- compared with the one stored in the repository. This can either be done
- by retrieving the value of the password attribute and checking it
- locally or by performing an LDAP "compare" operation, where the supplied
- password is passed to the server for comparison and the real password
- value is never retrieved.</para>
- </listitem>
- </itemizedlist></para>
- <section xml:id="ldap-ldap-authenticators-common">
- <info>
- <title>Common Functionality</title>
- </info>
- <para>Before it is possible to authenticate a user (by either strategy), the
- distinguished name (DN) has to be obtained from the login name supplied to the
- application. This can be done either by simple pattern-matching (by setting the
- <property>setUserDnPatterns</property> array property) or by setting the
- <property>userSearch</property> property. For the DN pattern-matching
- approach, a standard Java pattern format is used, and the login name will be
- substituted for the parameter <parameter>{0}</parameter>. The pattern should be
- relative to the DN that the configured
- <interfacename>SpringSecurityContextSource</interfacename> will bind to (see
- the section on <link linkend="ldap-context-source">connecting to the LDAP
- server</link> for more information on this). For example, if you are using
- an LDAP server with the URL
- <literal>ldap://monkeymachine.co.uk/dc=springframework,dc=org</literal>, and
- have a pattern <literal>uid={0},ou=greatapes</literal>, then a login name of
- "gorilla" will map to a DN
- <literal>uid=gorilla,ou=greatapes,dc=springframework,dc=org</literal>. Each
- configured DN pattern will be tried in turn until a match is found. For
- information on using a search, see the section on <link
- linkend="ldap-searchobjects">search objects</link> below. A combination of
- the two approaches can also be used - the patterns will be checked first and if
- no matching DN is found, the search will be used.</para>
- </section>
- <section xml:id="ldap-ldap-authenticators-bind">
- <info>
- <title>BindAuthenticator</title>
- </info>
- <para>The class <classname>BindAuthenticator</classname> in the package
- <filename>org.springframework.security.ldap.authentication</filename>
- implements the bind authentication strategy. It simply attempts to bind as the
- user.</para>
- </section>
- <section xml:id="ldap-ldap-authenticators-password">
- <info>
- <title>PasswordComparisonAuthenticator</title>
- </info>
- <para>The class <classname>PasswordComparisonAuthenticator</classname> implements
- the password comparison authentication strategy.</para>
- </section>
- <section xml:id="ldap-ldap-authenticators-active-directory">
- <info>
- <title>Active Directory Authentication</title>
- </info>
- <para>In addition to standard LDAP authentication (binding with a DN), Active
- Directory has its own non-standard syntax for user authentication.</para>
- </section>
- </section>
- <section xml:id="ldap-context-source">
- <info>
- <title>Connecting to the LDAP Server</title>
- </info>
- <para>The beans discussed above have to be able to connect to the server. They both have
- to be supplied with a <interfacename>SpringSecurityContextSource</interfacename>
- which is an extension of Spring LDAP's <interfacename>ContextSource</interfacename>.
- Unless you have special requirements, you will usually configure a
- <classname>DefaultSpringSecurityContextSource</classname> bean, which can be
- configured with the URL of your LDAP server and optionally with the username and
- password of a "manager" user which will be used by default when binding to the
- server (instead of binding anonymously). For more information read the Javadoc for
- this class and for Spring LDAP's <classname>AbstractContextSource</classname>.
- </para>
- </section>
- <section xml:id="ldap-searchobjects">
- <info>
- <title>LDAP Search Objects</title>
- </info>
- <para>Often a more complicated strategy than simple DN-matching is required to
- locate a user entry in the directory. This can be encapsulated in an
- <interfacename>LdapUserSearch</interfacename> instance which can be supplied to
- the authenticator implementations, for example, to allow them to locate a user. The
- supplied implementation is <classname>FilterBasedLdapUserSearch</classname>.</para>
- <section xml:id="ldap-searchobjects-filter">
- <info>
- <title xml:id="ldap-searchobjects-filter-based">
- <classname>FilterBasedLdapUserSearch</classname>
- </title>
- </info>
- <para>This bean uses an LDAP filter to match the user object in the directory. The
- process is explained in the Javadoc for the corresponding search method on the
- <link xmlns:xlink="http://www.w3.org/1999/xlink"
- xlink:href="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/DirContext.html#search(javax.naming.Name,%20java.lang.String,%20java.lang.Object[],%20javax.naming.directory.SearchControls)"
- >JDK DirContext class</link>. As explained there, the search filter can be
- supplied with parameters. For this class, the only valid parameter is
- <parameter>{0}</parameter> which will be replaced with the user's login
- name.</para>
- </section>
- </section>
- <section xml:id="ldap-authorities">
- <title>LdapAuthoritiesPopulator</title>
- <para> After authenticating the user successfully, the
- <classname>LdapAuthenticationProvider</classname> will attempt to load a set of
- authorities for the user by calling the configured
- <interfacename>LdapAuthoritiesPopulator</interfacename> bean. The
- <classname>DefaultLdapAuthoritiesPopulator</classname> is an implementation
- which will load the authorities by searching the directory for groups of which the
- user is a member (typically these will be <literal>groupOfNames</literal> or
- <literal>groupOfUniqueNames</literal> entries in the directory). Consult the
- Javadoc for this class for more details on how it works. </para>
- <para>If you want to use LDAP only for authentication, but load the authorities from a
- difference source (such as a database) then you can provide your own implementation
- of this interface and inject that instead.</para>
- </section>
- <section xml:id="ldap-bean-config">
- <info>
- <title>Spring Bean Configuration</title>
- </info>
- <para>A typical configuration, using some of the beans we've discussed here, might look
- like this: <programlisting><![CDATA[
- <bean id="contextSource"
- class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
- <constructor-arg value="ldap://monkeymachine:389/dc=springframework,dc=org"/>
- <property name="userDn" value="cn=manager,dc=springframework,dc=org"/>
- <property name="password" value="password"/>
- </bean>
- <bean id="ldapAuthProvider"
- class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
- <constructor-arg>
- <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
- <constructor-arg ref="contextSource"/>
- <property name="userDnPatterns">
- <list><value>uid={0},ou=people</value></list>
- </property>
- </bean>
- </constructor-arg>
- <constructor-arg>
- <bean
- class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
- <constructor-arg ref="contextSource"/>
- <constructor-arg value="ou=groups"/>
- <property name="groupRoleAttribute" value="ou"/>
- </bean>
- </constructor-arg>
- </bean>]]>
- </programlisting> This would set up the provider to access an LDAP server
- with URL <literal>ldap://monkeymachine:389/dc=springframework,dc=org</literal>.
- Authentication will be performed by attempting to bind with the DN
- <literal>uid=<user-login-name>,ou=people,dc=springframework,dc=org</literal>.
- After successful authentication, roles will be assigned to the user by searching
- under the DN <literal>ou=groups,dc=springframework,dc=org</literal> with the default
- filter <literal>(member=<user's-DN>)</literal>. The role name will be taken
- from the <quote>ou</quote> attribute of each match.</para>
- <para>To configure a user search object, which uses the filter
- <literal>(uid=<user-login-name>)</literal> for use instead of the
- DN-pattern (or in addition to it), you would configure the following bean <programlisting><![CDATA[
- <bean id="userSearch"
- class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
- <constructor-arg index="0" value=""/>
- <constructor-arg index="1" value="(uid={0})"/>
- <constructor-arg index="2" ref="contextSource" />
- </bean> ]]>
- </programlisting> and use it by setting the
- <classname>BindAuthenticator</classname> bean's <property>userSearch</property>
- property. The authenticator would then call the search object to obtain the correct
- user's DN before attempting to bind as this user.</para>
- </section>
- <section xml:id="ldap-custom-user-details">
- <title>LDAP Attributes and Customized UserDetails</title>
- <para> The net result of an authentication using
- <classname>LdapAuthenticationProvider</classname> is the same as a normal Spring
- Security authentication using the standard
- <interfacename>UserDetailsService</interfacename> interface. A
- <interfacename>UserDetails</interfacename> object is created and stored in the
- returned <interfacename>Authentication</interfacename> object. As with using a
- <interfacename>UserDetailsService</interfacename>, a common requirement is to be
- able to customize this implementation and add extra properties. When using LDAP,
- these will normally be attributes from the user entry. The creation of the
- <interfacename>UserDetails</interfacename> object is controlled by the
- provider's <interfacename>UserDetailsContextMapper</interfacename> strategy, which
- is responsible for mapping user objects to and from LDAP context data: <programlisting><![CDATA[
- public interface UserDetailsContextMapper {
- UserDetails mapUserFromContext(DirContextOperations ctx, String username,
- Collection<GrantedAuthority> authorities);
- void mapUserToContext(UserDetails user, DirContextAdapter ctx);
- }]]>
- </programlisting> Only the first method is relevant for authentication. If you
- provide an implementation of this interface and inject it into the
- <classname>LdapAuthenticationProvider</classname>, you have control over exactly how
- the UserDetails object is created. The first parameter is an instance of Spring
- LDAP's <interfacename>DirContextOperations</interfacename> which gives you access to
- the LDAP attributes which were loaded during authentication. The
- <literal>username</literal> parameter is the name used to authenticate and the final
- parameter is the collection of authorities loaded for the user by the configured
- <interfacename>LdapAuthoritiesPopulator</interfacename>. </para>
- <para> The way the context data is loaded varies slightly depending on the type of
- authentication you are using. With the <classname>BindAuthenticator</classname>, the
- context returned from the bind operation will be used to read the attributes,
- otherwise the data will be read using the standard context obtained from the
- configured <interfacename>ContextSource</interfacename> (when a search is configured
- to locate the user, this will be the data returned by the search object). </para>
- </section>
- </section>
- </chapter>
|