12. Additional Topics

In this part we cover features which require knowledge of previous chapters as well as some of the more advanced and less-commonly used features of the framework.

12.1 Domain Object Security (ACLs)

12.1.1 Overview

Complex applications often will find the need to define access permissions not simply at a web request or method invocation level. Instead, security decisions need to comprise both who (Authentication), where (MethodInvocation) and what (SomeDomainObject). In other words, authorization decisions also need to consider the actual domain object instance subject of a method invocation.

Imagine you’re designing an application for a pet clinic. There will be two main groups of users of your Spring-based application: staff of the pet clinic, as well as the pet clinic’s customers. The staff will have access to all of the data, whilst your customers will only be able to see their own customer records. To make it a little more interesting, your customers can allow other users to see their customer records, such as their "puppy preschool" mentor or president of their local "Pony Club". Using Spring Security as the foundation, you have several approaches that can be used:

  • Write your business methods to enforce the security. You could consult a collection within the Customer domain object instance to determine which users have access. By using the SecurityContextHolder.getContext().getAuthentication(), you’ll be able to access the Authentication object.
  • Write an AccessDecisionVoter to enforce the security from the GrantedAuthority[] s stored in the Authentication object. This would mean your AuthenticationManager would need to populate the Authentication with custom GrantedAuthority[]s representing each of the Customer domain object instances the principal has access to.
  • Write an AccessDecisionVoter to enforce the security and open the target Customer domain object directly. This would mean your voter needs access to a DAO that allows it to retrieve the Customer object. It would then access the Customer object’s collection of approved users and make the appropriate decision.

Each one of these approaches is perfectly legitimate. However, the first couples your authorization checking to your business code. The main problems with this include the enhanced difficulty of unit testing and the fact it would be more difficult to reuse the Customer authorization logic elsewhere. Obtaining the GrantedAuthority[] s from the Authentication object is also fine, but will not scale to large numbers of Customer s. If a user might be able to access 5,000 Customer s (unlikely in this case, but imagine if it were a popular vet for a large Pony Club!) the amount of memory consumed and time required to construct the Authentication object would be undesirable. The final method, opening the Customer directly from external code, is probably the best of the three. It achieves separation of concerns, and doesn’t misuse memory or CPU cycles, but it is still inefficient in that both the AccessDecisionVoter and the eventual business method itself will perform a call to the DAO responsible for retrieving the Customer object. Two accesses per method invocation is clearly undesirable. In addition, with every approach listed you’ll need to write your own access control list (ACL) persistence and business logic from scratch.

Fortunately, there is another alternative, which we’ll talk about below.

12.1.2 Key Concepts

Spring Security’s ACL services are shipped in the spring-security-acl-xxx.jar. You will need to add this JAR to your classpath to use Spring Security’s domain object instance security capabilities.

Spring Security’s domain object instance security capabilities centre on the concept of an access control list (ACL). Every domain object instance in your system has its own ACL, and the ACL records details of who can and can’t work with that domain object. With this in mind, Spring Security delivers three main ACL-related capabilities to your application:

  • A way of efficiently retrieving ACL entries for all of your domain objects (and modifying those ACLs)
  • A way of ensuring a given principal is permitted to work with your objects, before methods are called
  • A way of ensuring a given principal is permitted to work with your objects (or something they return), after methods are called

As indicated by the first bullet point, one of the main capabilities of the Spring Security ACL module is providing a high-performance way of retrieving ACLs. This ACL repository capability is extremely important, because every domain object instance in your system might have several access control entries, and each ACL might inherit from other ACLs in a tree-like structure (this is supported out-of-the-box by Spring Security, and is very commonly used). Spring Security’s ACL capability has been carefully designed to provide high performance retrieval of ACLs, together with pluggable caching, deadlock-minimizing database updates, independence from ORM frameworks (we use JDBC directly), proper encapsulation, and transparent database updating.

Given databases are central to the operation of the ACL module, let’s explore the four main tables used by default in the implementation. The tables are presented below in order of size in a typical Spring Security ACL deployment, with the table with the most rows listed last:

  • ACL_SID allows us to uniquely identify any principal or authority in the system ("SID" stands for "security identity"). The only columns are the ID, a textual representation of the SID, and a flag to indicate whether the textual representation refers to a principal name or a GrantedAuthority. Thus, there is a single row for each unique principal or GrantedAuthority. When used in the context of receiving a permission, a SID is generally called a "recipient".
  • ACL_CLASS allows us to uniquely identify any domain object class in the system. The only columns are the ID and the Java class name. Thus, there is a single row for each unique Class we wish to store ACL permissions for.
  • ACL_OBJECT_IDENTITY stores information for each unique domain object instance in the system. Columns include the ID, a foreign key to the ACL_CLASS table, a unique identifier so we know which ACL_CLASS instance we’re providing information for, the parent, a foreign key to the ACL_SID table to represent the owner of the domain object instance, and whether we allow ACL entries to inherit from any parent ACL. We have a single row for every domain object instance we’re storing ACL permissions for.
  • Finally, ACL_ENTRY stores the individual permissions assigned to each recipient. Columns include a foreign key to the ACL_OBJECT_IDENTITY, the recipient (ie a foreign key to ACL_SID), whether we’ll be auditing or not, and the integer bit mask that represents the actual permission being granted or denied. We have a single row for every recipient that receives a permission to work with a domain object.

As mentioned in the last paragraph, the ACL system uses integer bit masking. Don’t worry, you need not be aware of the finer points of bit shifting to use the ACL system, but suffice to say that we have 32 bits we can switch on or off. Each of these bits represents a permission, and by default the permissions are read (bit 0), write (bit 1), create (bit 2), delete (bit 3) and administer (bit 4). It’s easy to implement your own Permission instance if you wish to use other permissions, and the remainder of the ACL framework will operate without knowledge of your extensions.

It is important to understand that the number of domain objects in your system has absolutely no bearing on the fact we’ve chosen to use integer bit masking. Whilst you have 32 bits available for permissions, you could have billions of domain object instances (which will mean billions of rows in ACL_OBJECT_IDENTITY and quite probably ACL_ENTRY). We make this point because we’ve found sometimes people mistakenly believe they need a bit for each potential domain object, which is not the case.

Now that we’ve provided a basic overview of what the ACL system does, and what it looks like at a table structure, let’s explore the key interfaces. The key interfaces are:

  • Acl: Every domain object has one and only one Acl object, which internally holds the AccessControlEntry s as well as knows the owner of the Acl. An Acl does not refer directly to the domain object, but instead to an ObjectIdentity. The Acl is stored in the ACL_OBJECT_IDENTITY table.
  • AccessControlEntry: An Acl holds multiple AccessControlEntry s, which are often abbreviated as ACEs in the framework. Each ACE refers to a specific tuple of Permission, Sid and Acl. An ACE can also be granting or non-granting and contain audit settings. The ACE is stored in the ACL_ENTRY table.
  • Permission: A permission represents a particular immutable bit mask, and offers convenience functions for bit masking and outputting information. The basic permissions presented above (bits 0 through 4) are contained in the BasePermission class.
  • Sid: The ACL module needs to refer to principals and GrantedAuthority[] s. A level of indirection is provided by the Sid interface, which is an abbreviation of "security identity". Common classes include PrincipalSid (to represent the principal inside an Authentication object) and GrantedAuthoritySid. The security identity information is stored in the ACL_SID table.
  • ObjectIdentity: Each domain object is represented internally within the ACL module by an ObjectIdentity. The default implementation is called ObjectIdentityImpl.
  • AclService: Retrieves the Acl applicable for a given ObjectIdentity. In the included implementation (JdbcAclService), retrieval operations are delegated to a LookupStrategy. The LookupStrategy provides a highly optimized strategy for retrieving ACL information, using batched retrievals (BasicLookupStrategy) and supporting custom implementations that leverage materialized views, hierarchical queries and similar performance-centric, non-ANSI SQL capabilities.
  • MutableAclService: Allows a modified Acl to be presented for persistence. It is not essential to use this interface if you do not wish.

Please note that our out-of-the-box AclService and related database classes all use ANSI SQL. This should therefore work with all major databases. At the time of writing, the system had been successfully tested using Hypersonic SQL, PostgreSQL, Microsoft SQL Server and Oracle.

Two samples ship with Spring Security that demonstrate the ACL module. The first is the Contacts Sample, and the other is the Document Management System (DMS) Sample. We suggest taking a look over these for examples.

12.1.3 Getting Started

To get starting using Spring Security’s ACL capability, you will need to store your ACL information somewhere. This necessitates the instantiation of a DataSource using Spring. The DataSource is then injected into a JdbcMutableAclService and BasicLookupStrategy instance. The latter provides high-performance ACL retrieval capabilities, and the former provides mutator capabilities. Refer to one of the samples that ship with Spring Security for an example configuration. You’ll also need to populate the database with the four ACL-specific tables listed in the last section (refer to the ACL samples for the appropriate SQL statements).

Once you’ve created the required schema and instantiated JdbcMutableAclService, you’ll next need to ensure your domain model supports interoperability with the Spring Security ACL package. Hopefully ObjectIdentityImpl will prove sufficient, as it provides a large number of ways in which it can be used. Most people will have domain objects that contain a public Serializable getId() method. If the return type is long, or compatible with long (eg an int), you will find you need not give further consideration to ObjectIdentity issues. Many parts of the ACL module rely on long identifiers. If you’re not using long (or an int, byte etc), there is a very good chance you’ll need to reimplement a number of classes. We do not intend to support non-long identifiers in Spring Security’s ACL module, as longs are already compatible with all database sequences, the most common identifier data type, and are of sufficient length to accommodate all common usage scenarios.

The following fragment of code shows how to create an Acl, or modify an existing Acl:

// Prepare the information we'd like in our access control entry (ACE)
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;

// Create or update the relevant ACL
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}

// Now grant some permissions via an access control entry (ACE)
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);

In the example above, we’re retrieving the ACL associated with the "Foo" domain object with identifier number 44. We’re then adding an ACE so that a principal named "Samantha" can "administer" the object. The code fragment is relatively self-explanatory, except the insertAce method. The first argument to the insertAce method is determining at what position in the Acl the new entry will be inserted. In the example above, we’re just putting the new ACE at the end of the existing ACEs. The final argument is a Boolean indicating whether the ACE is granting or denying. Most of the time it will be granting (true), but if it is denying (false), the permissions are effectively being blocked.

Spring Security does not provide any special integration to automatically create, update or delete ACLs as part of your DAO or repository operations. Instead, you will need to write code like shown above for your individual domain objects. It’s worth considering using AOP on your services layer to automatically integrate the ACL information with your services layer operations. We’ve found this quite an effective approach in the past.

Once you’ve used the above techniques to store some ACL information in the database, the next step is to actually use the ACL information as part of authorization decision logic. You have a number of choices here. You could write your own AccessDecisionVoter or AfterInvocationProvider that respectively fires before or after a method invocation. Such classes would use AclService to retrieve the relevant ACL and then call Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode) to decide whether permission is granted or denied. Alternately, you could use our AclEntryVoter, AclEntryAfterInvocationProvider or AclEntryAfterInvocationCollectionFilteringProvider classes. All of these classes provide a declarative-based approach to evaluating ACL information at runtime, freeing you from needing to write any code. Please refer to the sample applications to learn how to use these classes.

12.2 Pre-Authentication Scenarios

There are situations where you want to use Spring Security for authorization, but the user has already been reliably authenticated by some external system prior to accessing the application. We refer to these situations as "pre-authenticated" scenarios. Examples include X.509, Siteminder and authentication by the Java EE container in which the application is running. When using pre-authentication, Spring Security has to

  • Identify the user making the request.
  • Obtain the authorities for the user.

The details will depend on the external authentication mechanism. A user might be identified by their certificate information in the case of X.509, or by an HTTP request header in the case of Siteminder. If relying on container authentication, the user will be identified by calling the getUserPrincipal() method on the incoming HTTP request. In some cases, the external mechanism may supply role/authority information for the user but in others the authorities must be obtained from a separate source, such as a UserDetailsService.

12.2.1 Pre-Authentication Framework Classes

Because most pre-authentication mechanisms follow the same pattern, Spring Security has a set of classes which provide an internal framework for implementing pre-authenticated authentication providers. This removes duplication and allows new implementations to be added in a structured fashion, without having to write everything from scratch. You don’t need to know about these classes if you want to use something like X.509 authentication, as it already has a namespace configuration option which is simpler to use and get started with. If you need to use explicit bean configuration or are planning on writing your own implementation then an understanding of how the provided implementations work will be useful. You will find classes under the org.springframework.security.web.authentication.preauth. We just provide an outline here so you should consult the Javadoc and source where appropriate.

AbstractPreAuthenticatedProcessingFilter

This class will check the current contents of the security context and, if empty, it will attempt to extract user information from the HTTP request and submit it to the AuthenticationManager. Subclasses override the following methods to obtain this information:

protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);

protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);

After calling these, the filter will create a PreAuthenticatedAuthenticationToken containing the returned data and submit it for authentication. By "authentication" here, we really just mean further processing to perhaps load the user’s authorities, but the standard Spring Security authentication architecture is followed.

Like other Spring Security authentication filters, the pre-authentication filter has an authenticationDetailsSource property which by default will create a WebAuthenticationDetails object to store additional information such as the session-identifier and originating IP address in the details property of the Authentication object. In cases where user role information can be obtained from the pre-authentication mechanism, the data is also stored in this property, with the details implementing the GrantedAuthoritiesContainer interface. This enables the authentication provider to read the authorities which were externally allocated to the user. We’ll look at a concrete example next.

J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource

If the filter is configured with an authenticationDetailsSource which is an instance of this class, the authority information is obtained by calling the isUserInRole(String role) method for each of a pre-determined set of "mappable roles". The class gets these from a configured MappableAttributesRetriever. Possible implementations include hard-coding a list in the application context and reading the role information from the <security-role> information in a web.xml file. The pre-authentication sample application uses the latter approach.

There is an additional stage where the roles (or attributes) are mapped to Spring Security GrantedAuthority objects using a configured Attributes2GrantedAuthoritiesMapper. The default will just add the usual ROLE_ prefix to the names, but it gives you full control over the behaviour.

PreAuthenticatedAuthenticationProvider

The pre-authenticated provider has little more to do than load the UserDetails object for the user. It does this by delegating to an AuthenticationUserDetailsService. The latter is similar to the standard UserDetailsService but takes an Authentication object rather than just user name:

public interface AuthenticationUserDetailsService {
    UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}

This interface may have also other uses but with pre-authentication it allows access to the authorities which were packaged in the Authentication object, as we saw in the previous section. The PreAuthenticatedGrantedAuthoritiesUserDetailsService class does this. Alternatively, it may delegate to a standard UserDetailsService via the UserDetailsByNameServiceWrapper implementation.

Http403ForbiddenEntryPoint

The AuthenticationEntryPoint was discussed in the technical overview chapter. Normally it is responsible for kick-starting the authentication process for an unauthenticated user (when they try to access a protected resource), but in the pre-authenticated case this doesn’t apply. You would only configure the ExceptionTranslationFilter with an instance of this class if you aren’t using pre-authentication in combination with other authentication mechanisms. It will be called if the user is rejected by the AbstractPreAuthenticatedProcessingFilter resulting in a null authentication. It always returns a 403-forbidden response code if called.

12.2.2 Concrete Implementations

X.509 authentication is covered in its own chapter. Here we’ll look at some classes which provide support for other pre-authenticated scenarios.

Request-Header Authentication (Siteminder)

An external authentication system may supply information to the application by setting specific headers on the HTTP request. A well-known example of this is Siteminder, which passes the username in a header called SM_USER. This mechanism is supported by the class RequestHeaderAuthenticationFilter which simply extracts the username from the header. It defaults to using the name SM_USER as the header name. See the Javadoc for more details.

[Tip]Tip

Note that when using a system like this, the framework performs no authentication checks at all and it is extremely important that the external system is configured properly and protects all access to the application. If an attacker is able to forge the headers in their original request without this being detected then they could potentially choose any username they wished.

Siteminder Example Configuration

A typical configuration using this filter would look like this:

<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>

<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>

<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
    <bean id="userDetailsServiceWrapper"
        class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <property name="userDetailsService" ref="userDetailsService"/>
    </bean>
</property>
</bean>

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

We’ve assumed here that the security namespace is being used for configuration. It’s also assumed that you have added a UserDetailsService (called "userDetailsService") to your configuration to load the user’s roles.

Java EE Container Authentication

The class J2eePreAuthenticatedProcessingFilter will extract the username from the userPrincipal property of the HttpServletRequest. Use of this filter would usually be combined with the use of Java EE roles as described above in the section called “J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource”.

There is a sample application in the codebase which uses this approach, so get hold of the code from github and have a look at the application context file if you are interested. The code is in the samples/xml/preauth directory.

12.3 LDAP Authentication

12.3.1 Overview

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.

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.

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: http://www.zytrax.com/books/ldap/. 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.

When using LDAP authentication, it is important to ensure that you configure LDAP connection pooling properly. If you are unfamiliar with how to do this, you can refer to the Java LDAP documentation.

12.3.2 Using LDAP with Spring Security

LDAP authentication in Spring Security can be roughly divided into the following stages.

  • Obtaining the unique LDAP "Distinguished Name", 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. So a user might enter the name "joe" when logging in, but the actual name used to authenticate to LDAP will be the full DN, such as uid=joe,ou=users,dc=spring,dc=io.
  • Authenticating the user, either by "binding" as that user or by performing a remote "compare" operation of the user’s password against the password attribute in the directory entry for the DN.
  • Loading the list of authorities for the user.

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.

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).

12.3.3 Configuring an LDAP Server

The first thing you need to do is configure the server against which authentication should take place. This is done using the <ldap-server> element from the security namespace. This can be configured to point at an external LDAP server, using the url attribute:

<ldap-server url="ldap://springframework.org:389/dc=springframework,dc=org" />

Using an Embedded Test Server

The <ldap-server> 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 url attribute:

<ldap-server root="dc=springframework,dc=org"/>

Here we’ve specified that the root DIT of the directory should be "dc=springframework,dc=org", 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 ldif attribute, which defines an LDIF resource to be loaded:

<ldap-server ldif="classpath:users.ldif" />

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.

Using Bind Authentication

This is the most common LDAP authentication scenario.

<ldap-authentication-provider user-dn-pattern="uid={0},ou=people"/>

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:

<ldap-authentication-provider user-search-filter="(uid={0})"
    user-search-base="ou=people"/>

If used with the server definition above, this would perform a search under the DN ou=people,dc=springframework,dc=org using the value of the user-search-filter attribute as a filter. Again the user login name is substituted for the parameter in the filter name, so it will search for an entry with the uid attribute equal to the user name. If user-search-base isn’t supplied, the search will be performed from the root.

Loading Authorities

How authorities are loaded from groups in the LDAP directory is controlled by the following attributes.

  • group-search-base. Defines the part of the directory tree under which group searches should be performed.
  • group-role-attribute. The attribute which contains the name of the authority defined by the group entry. Defaults to cn
  • group-search-filter. The filter which is used to search for group membership. The default is uniqueMember={0}, corresponding to the groupOfUniqueNames LDAP class [19]. In this case, the substituted parameter is the full distinguished name of the user. The parameter {1} can be used if you want to filter on the login name.

So if we used the following configuration

<ldap-authentication-provider user-dn-pattern="uid={0},ou=people"
        group-search-base="ou=groups" />

and authenticated successfully as user "ben", the subsequent loading of authorities would perform a search under the directory entry ou=groups,dc=springframework,dc=org, looking for entries which contain the attribute uniqueMember with value uid=ben,ou=people,dc=springframework,dc=org. By default the authority names will have the prefix ROLE_ prepended. You can change this using the role-prefix attribute. If you don’t want any prefix, use role-prefix="none". For more information on loading authorities, see the Javadoc for the DefaultLdapAuthoritiesPopulator class.

12.3.4 Implementation Classes

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.

The main LDAP provider class, LdapAuthenticationProvider, doesn’t actually do much itself but delegates the work to two other beans, an LdapAuthenticator and an LdapAuthoritiesPopulator which are responsible for authenticating the user and retrieving the user’s set of GrantedAuthority s respectively.

LdapAuthenticator Implementations

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.

There are currently two authentication strategies supplied with Spring Security:

  • Authentication directly to the LDAP server ("bind" authentication).
  • 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.
Common Functionality

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 setUserDnPatterns array property) or by setting the userSearch property. For the DN pattern-matching approach, a standard Java pattern format is used, and the login name will be substituted for the parameter {0}. The pattern should be relative to the DN that the configured SpringSecurityContextSource will bind to (see the section on connecting to the LDAP server for more information on this). For example, if you are using an LDAP server with the URL ldap://monkeymachine.co.uk/dc=springframework,dc=org, and have a pattern uid={0},ou=greatapes, then a login name of "gorilla" will map to a DN uid=gorilla,ou=greatapes,dc=springframework,dc=org. Each configured DN pattern will be tried in turn until a match is found. For information on using a search, see the section on search objects 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.

BindAuthenticator

The class BindAuthenticator in the package org.springframework.security.ldap.authentication implements the bind authentication strategy. It simply attempts to bind as the user.

PasswordComparisonAuthenticator

The class PasswordComparisonAuthenticator implements the password comparison authentication strategy.

Connecting to the LDAP Server

The beans discussed above have to be able to connect to the server. They both have to be supplied with a SpringSecurityContextSource which is an extension of Spring LDAP’s ContextSource. Unless you have special requirements, you will usually configure a DefaultSpringSecurityContextSource 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 AbstractContextSource.

LDAP Search Objects

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 LdapUserSearch instance which can be supplied to the authenticator implementations, for example, to allow them to locate a user. The supplied implementation is FilterBasedLdapUserSearch.

FilterBasedLdapUserSearch

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 JDK DirContext class. As explained there, the search filter can be supplied with parameters. For this class, the only valid parameter is {0} which will be replaced with the user’s login name.

LdapAuthoritiesPopulator

After authenticating the user successfully, the LdapAuthenticationProvider will attempt to load a set of authorities for the user by calling the configured LdapAuthoritiesPopulator bean. The DefaultLdapAuthoritiesPopulator 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 groupOfNames or groupOfUniqueNames entries in the directory). Consult the Javadoc for this class for more details on how it works.

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.

Spring Bean Configuration

A typical configuration, using some of the beans we’ve discussed here, might look like this:

<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>

This would set up the provider to access an LDAP server with URL ldap://monkeymachine:389/dc=springframework,dc=org. Authentication will be performed by attempting to bind with the DN uid=<user-login-name>,ou=people,dc=springframework,dc=org. After successful authentication, roles will be assigned to the user by searching under the DN ou=groups,dc=springframework,dc=org with the default filter (member=<user’s-DN>). The role name will be taken from the "ou" attribute of each match.

To configure a user search object, which uses the filter (uid=<user-login-name>) for use instead of the DN-pattern (or in addition to it), you would configure the following bean

<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>

and use it by setting the BindAuthenticator bean’s userSearch property. The authenticator would then call the search object to obtain the correct user’s DN before attempting to bind as this user.

LDAP Attributes and Customized UserDetails

The net result of an authentication using LdapAuthenticationProvider is the same as a normal Spring Security authentication using the standard UserDetailsService interface. A UserDetails object is created and stored in the returned Authentication object. As with using a UserDetailsService, 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 UserDetails object is controlled by the provider’s UserDetailsContextMapper strategy, which is responsible for mapping user objects to and from LDAP context data:

public interface UserDetailsContextMapper {

UserDetails mapUserFromContext(DirContextOperations ctx, String username,
        Collection<GrantedAuthority> authorities);

void mapUserToContext(UserDetails user, DirContextAdapter ctx);
}

Only the first method is relevant for authentication. If you provide an implementation of this interface and inject it into the LdapAuthenticationProvider, you have control over exactly how the UserDetails object is created. The first parameter is an instance of Spring LDAP’s DirContextOperations which gives you access to the LDAP attributes which were loaded during authentication. The username parameter is the name used to authenticate and the final parameter is the collection of authorities loaded for the user by the configured LdapAuthoritiesPopulator.

The way the context data is loaded varies slightly depending on the type of authentication you are using. With the BindAuthenticator, 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 ContextSource (when a search is configured to locate the user, this will be the data returned by the search object).

12.3.5 Active Directory Authentication

Active Directory supports its own non-standard authentication options, and the normal usage pattern doesn’t fit too cleanly with the standard LdapAuthenticationProvider. Typically authentication is performed using the domain username (in the form user@domain), rather than using an LDAP distinguished name. To make this easier, Spring Security 3.1 has an authentication provider which is customized for a typical Active Directory setup.

ActiveDirectoryLdapAuthenticationProvider

Configuring ActiveDirectoryLdapAuthenticationProvider is quite straightforward. You just need to supply the domain name and an LDAP URL supplying the address of the server [20]. An example configuration would then look like this:

<bean id="adAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <constructor-arg value="mydomain.com" />
    <constructor-arg value="ldap://adserver.mydomain.com/" />
</bean>
}

Note that there is no need to specify a separate ContextSource in order to define the server location - the bean is completely self-contained. A user named "Sharon", for example, would then be able to authenticate by entering either the username sharon or the full Active Directory userPrincipalName, namely [email protected]. The user’s directory entry will then be located, and the attributes returned for possible use in customizing the created UserDetails object (a UserDetailsContextMapper can be injected for this purpose, as described above). All interaction with the directory takes place with the identity of the user themselves. There is no concept of a "manager" user.

By default, the user authorities are obtained from the memberOf attribute values of the user entry. The authorities allocated to the user can again be customized using a UserDetailsContextMapper. You can also inject a GrantedAuthoritiesMapper into the provider instance to control the authorities which end up in the Authentication object.

Active Directory Error Codes

By default, a failed result will cause a standard Spring Security BadCredentialsException. If you set the property convertSubErrorCodesToExceptions to true, the exception messages will be parsed to attempt to extract the Active Directory-specific error code and raise a more specific exception. Check the class Javadoc for more information.

12.4 OAuth 2.0 Login — Advanced Configuration

HttpSecurity.oauth2Login() provides a number of configuration options for customizing OAuth 2.0 Login. The main configuration options are grouped into their protocol endpoint counterparts.

For example, oauth2Login().authorizationEndpoint() allows configuring the Authorization Endpoint, whereas oauth2Login().tokenEndpoint() allows configuring the Token Endpoint.

The following code shows an example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .authorizationEndpoint()
                    ...
                .redirectionEndpoint()
                    ...
                .tokenEndpoint()
                    ...
                .userInfoEndpoint()
                    ...
    }
}

The main goal of the oauth2Login() DSL was to closely align with the naming, as defined in the specifications.

The OAuth 2.0 Authorization Framework defines the Protocol Endpoints as follows:

The authorization process utilizes two authorization server endpoints (HTTP resources):

  • Authorization Endpoint: Used by the client to obtain authorization from the resource owner via user-agent redirection.
  • Token Endpoint: Used by the client to exchange an authorization grant for an access token, typically with client authentication.

As well as one client endpoint:

  • Redirection Endpoint: Used by the authorization server to return responses containing authorization credentials to the client via the resource owner user-agent.

The OpenID Connect Core 1.0 specification defines the UserInfo Endpoint as follows:

The UserInfo Endpoint is an OAuth 2.0 Protected Resource that returns claims about the authenticated end-user. To obtain the requested claims about the end-user, the client makes a request to the UserInfo Endpoint by using an access token obtained through OpenID Connect Authentication. These claims are normally represented by a JSON object that contains a collection of name-value pairs for the claims.

The following code shows the complete configuration options available for the oauth2Login() DSL:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .clientRegistrationRepository(this.clientRegistrationRepository())
                .authorizedClientRepository(this.authorizedClientRepository())
                .authorizedClientService(this.authorizedClientService())
                .loginPage("/login")
                .authorizationEndpoint()
                    .baseUri(this.authorizationRequestBaseUri())
                    .authorizationRequestRepository(this.authorizationRequestRepository())
                    .authorizationRequestResolver(this.authorizationRequestResolver())
                    .and()
                .redirectionEndpoint()
                    .baseUri(this.authorizationResponseBaseUri())
                    .and()
                .tokenEndpoint()
                    .accessTokenResponseClient(this.accessTokenResponseClient())
                    .and()
                .userInfoEndpoint()
                    .userAuthoritiesMapper(this.userAuthoritiesMapper())
                    .userService(this.oauth2UserService())
                    .oidcUserService(this.oidcUserService())
                    .customUserType(GitHubOAuth2User.class, "github");
    }
}

The following sections go into more detail on each of the configuration options available:

12.4.1 OAuth 2.0 Login Page

By default, the OAuth 2.0 Login Page is auto-generated by the DefaultLoginPageGeneratingFilter. The default login page shows each configured OAuth Client with its ClientRegistration.clientName as a link, which is capable of initiating the Authorization Request (or OAuth 2.0 Login).

The link’s destination for each OAuth Client defaults to the following:

OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{registrationId}"

The following line shows an example:

<a href="/oauth2/authorization/google">Google</a>

To override the default login page, configure oauth2Login().loginPage() and (optionally) oauth2Login().authorizationEndpoint().baseUri().

The following listing shows an example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .loginPage("/login/oauth2")
                ...
                .authorizationEndpoint()
                    .baseUri("/login/oauth2/authorization")
                    ....
    }
}
[Important]Important

You need to provide a @Controller with a @RequestMapping("/login/oauth2") that is capable of rendering the custom login page.

[Tip]Tip

As noted earlier, configuring oauth2Login().authorizationEndpoint().baseUri() is optional. However, if you choose to customize it, ensure the link to each OAuth Client matches the authorizationEndpoint().baseUri().

The following line shows an example:

<a href="/login/oauth2/authorization/google">Google</a>

12.4.2 Redirection Endpoint

The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response (which contains the authorization credentials) to the client via the Resource Owner user-agent.

[Tip]Tip

OAuth 2.0 Login leverages the Authorization Code Grant. Therefore, the authorization credential is the authorization code.

The default Authorization Response baseUri (redirection endpoint) is /login/oauth2/code/*, which is defined in OAuth2LoginAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI.

If you would like to customize the Authorization Response baseUri, configure it as shown in the following example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .redirectionEndpoint()
                    .baseUri("/login/oauth2/callback/*")
                    ....
    }
}
[Important]Important

You also need to ensure the ClientRegistration.redirectUriTemplate matches the custom Authorization Response baseUri.

The following listing shows an example:

return CommonOAuth2Provider.GOOGLE.getBuilder("google")
    .clientId("google-client-id")
    .clientSecret("google-client-secret")
    .redirectUriTemplate("{baseUrl}/login/oauth2/callback/{registrationId}")
    .build();

12.4.3 UserInfo Endpoint

The UserInfo Endpoint includes a number of configuration options, as described in the following sub-sections:

Mapping User Authorities

After the user successfully authenticates with the OAuth 2.0 Provider, the OAuth2User.getAuthorities() (or OidcUser.getAuthorities()) may be mapped to a new set of GrantedAuthority instances, which will be supplied to OAuth2AuthenticationToken when completing the authentication.

[Tip]Tip

OAuth2AuthenticationToken.getAuthorities() is used for authorizing requests, such as in hasRole('USER') or hasRole('ADMIN').

There are a couple of options to choose from when mapping user authorities:

Using a GrantedAuthoritiesMapper

Provide an implementation of GrantedAuthoritiesMapper and configure it as shown in the following example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .userInfoEndpoint()
                    .userAuthoritiesMapper(this.userAuthoritiesMapper())
                    ...
    }

    private GrantedAuthoritiesMapper userAuthoritiesMapper() {
        return (authorities) -> {
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

            authorities.forEach(authority -> {
                if (OidcUserAuthority.class.isInstance(authority)) {
                    OidcUserAuthority oidcUserAuthority = (OidcUserAuthority)authority;

                    OidcIdToken idToken = oidcUserAuthority.getIdToken();
                    OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();

                    // Map the claims found in idToken and/or userInfo
                    // to one or more GrantedAuthority's and add it to mappedAuthorities

                } else if (OAuth2UserAuthority.class.isInstance(authority)) {
                    OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority)authority;

                    Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();

                    // Map the attributes found in userAttributes
                    // to one or more GrantedAuthority's and add it to mappedAuthorities

                }
            });

            return mappedAuthorities;
        };
    }
}

Alternatively, you may register a GrantedAuthoritiesMapper @Bean to have it automatically applied to the configuration, as shown in the following example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.oauth2Login();
    }

    @Bean
    public GrantedAuthoritiesMapper userAuthoritiesMapper() {
        ...
    }
}
Delegation-based strategy with OAuth2UserService

This strategy is advanced compared to using a GrantedAuthoritiesMapper, however, it’s also more flexible as it gives you access to the OAuth2UserRequest and OAuth2User (when using an OAuth 2.0 UserService) or OidcUserRequest and OidcUser (when using an OpenID Connect 1.0 UserService).

The OAuth2UserRequest (and OidcUserRequest) provides you access to the associated OAuth2AccessToken, which is very useful in the cases where the delegator needs to fetch authority information from a protected resource before it can map the custom authorities for the user.

The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .userInfoEndpoint()
                    .oidcUserService(this.oidcUserService())
                    ...
    }

    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        final OidcUserService delegate = new OidcUserService();

        return (userRequest) -> {
            // Delegate to the default implementation for loading a user
            OidcUser oidcUser = delegate.loadUser(userRequest);

            OAuth2AccessToken accessToken = userRequest.getAccessToken();
            Set<GrantedAuthority> mappedAuthorities = new HashSet<>();

            // TODO
            // 1) Fetch the authority information from the protected resource using accessToken
            // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities

            // 3) Create a copy of oidcUser but use the mappedAuthorities instead
            oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());

            return oidcUser;
        };
    }
}

Configuring a Custom OAuth2User

CustomUserTypesOAuth2UserService is an implementation of an OAuth2UserService that provides support for custom OAuth2User types.

If the default implementation (DefaultOAuth2User) does not suit your needs, you can define your own implementation of OAuth2User.

The following code demonstrates how you would register a custom OAuth2User type for GitHub:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .userInfoEndpoint()
                    .customUserType(GitHubOAuth2User.class, "github")
                    ...
    }
}

The following code shows an example of a custom OAuth2User type for GitHub:

public class GitHubOAuth2User implements OAuth2User {
    private List<GrantedAuthority> authorities =
        AuthorityUtils.createAuthorityList("ROLE_USER");
    private Map<String, Object> attributes;
    private String id;
    private String name;
    private String login;
    private String email;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public Map<String, Object> getAttributes() {
        if (this.attributes == null) {
            this.attributes = new HashMap<>();
            this.attributes.put("id", this.getId());
            this.attributes.put("name", this.getName());
            this.attributes.put("login", this.getLogin());
            this.attributes.put("email", this.getEmail());
        }
        return attributes;
    }

    public String getId() {
        return this.id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLogin() {
        return this.login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getEmail() {
        return this.email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
[Tip]Tip

id, name, login, and email are attributes returned in GitHub’s UserInfo Response. For detailed information returned from the UserInfo Endpoint, see the API documentation for "Get the authenticated user".

OAuth 2.0 UserService

DefaultOAuth2UserService is an implementation of an OAuth2UserService that supports standard OAuth 2.0 Provider’s.

[Note]Note

OAuth2UserService obtains the user attributes of the end-user (the resource owner) from the UserInfo Endpoint (by using the access token granted to the client during the authorization flow) and returns an AuthenticatedPrincipal in the form of an OAuth2User.

DefaultOAuth2UserService uses a RestOperations when requesting the user attributes at the UserInfo Endpoint.

If you need to customize the pre-processing of the UserInfo Request, you can provide DefaultOAuth2UserService.setRequestEntityConverter() with a custom Converter<OAuth2UserRequest, RequestEntity<?>>. The default implementation OAuth2UserRequestEntityConverter builds a RequestEntity representation of a UserInfo Request that sets the OAuth2AccessToken in the Authorization header by default.

On the other end, if you need to customize the post-handling of the UserInfo Response, you will need to provide DefaultOAuth2UserService.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error (400 Bad Request). It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error.

Whether you customize DefaultOAuth2UserService or provide your own implementation of OAuth2UserService, you’ll need to configure it as shown in the following example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .userInfoEndpoint()
                    .userService(this.oauth2UserService())
                    ...
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        ...
    }
}

OpenID Connect 1.0 UserService

OidcUserService is an implementation of an OAuth2UserService that supports OpenID Connect 1.0 Provider’s.

The OidcUserService leverages the DefaultOAuth2UserService when requesting the user attributes at the UserInfo Endpoint.

If you need to customize the pre-processing of the UserInfo Request and/or the post-handling of the UserInfo Response, you will need to provide OidcUserService.setOauth2UserService() with a custom configured DefaultOAuth2UserService.

Whether you customize OidcUserService or provide your own implementation of OAuth2UserService for OpenID Connect 1.0 Provider’s, you’ll need to configure it as shown in the following example:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .oauth2Login()
                .userInfoEndpoint()
                    .oidcUserService(this.oidcUserService())
                    ...
    }

    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        ...
    }
}


[19] Note that this is different from the default configuration of the underlying DefaultLdapAuthoritiesPopulator which uses member={0}.

[20] It is also possible to obtain the server’s IP address using a DNS lookup. This is not currently supported, but hopefully will be in a future version.