Broadleaf Commerce, an enterprise class, open source eCommerce framework is built on The Spring Framework. As such, Spring Security is a natural choice to provide security for Broadleaf Commerce, even when using an authentication mechanism like Active Directory. Broadleaf Commerce provides a few entities that make up the basic domain and foundation for security. These entities include:
- AdminUser (representing a user of the administrative portal)
- AdminRole (a named entity representing a mapping of AdminUser to multiple AdminPerssions)
- AdminPermission (a named entity representing a specific Broadleaf permission)
With this configuration you can manage security quite effectively from the Broadleaf database, requiring administrators to log in and gain access to features of the administrative console depending on their roles. However, many companies wish to maintain user credentials and roles in an LDAP server. Active Directory is a very popular LDAP server for many organizations. So how can one use Active Directory to authenticate and authorize administrative users in Broadleaf Commerce?
The answer goes back to Spring Security. Spring Security provides an LDAP module. It also provides an extension to specifically deal with Active Directory. It is a simple matter of configuration to secure the application with Spring Security and Active Directory. However, we need the principal and roles that are returned from LDAP to map to Broadleaf-specific users. In order to facilitate this, Broadleaf has implemented a bean called BroadleafActiveDirectoryUserDetailsMapper and a Servlet Filter called AdminExternalLoginStateFilter. These beans provide a hook for Spring Security to authenticate against LDAP, map the appropriate user details and roles, look up the local admin user from the Broadleaf database and map the roles (or Granted Authorities) from Active Directory to Broadleaf-specific role, and provision a user record in the Broadleaf database if one does not exist.
Assuming a successful login, after authentication, the *BroadleafActiveDirectoryUserDetailsMapper*, will map the roles to the authenticated user. It will also create a new instance of *BroadleafExternalAuthenticationUserDetails* (which extends Spring's UserDetails) and set that as the details of the Authentication object. This allows us to pass some extra information to Broadleaf like first name, last name, email, etc. After authentication, the *AdminExternalLoginStateFilter* will take the Authentication object from Spring, associated with the current user, and:
- Look up the user, by user name, from the database
- Create a new user if one does not exist
- Set the properties from the *BroadleafExternalAuthenticationUserDetails*
- Delete any roles that were associated with the user at last login
- Assign new roles to the user based on their current authentication
This allows Broadleaf to happily continue to operate with its own entities. But it also ensures that authentication happens via LDAP, and that all data roles associated with the user are entirely refreshed from the source system (i.e. LDAP) at the time of login.
There are some configuration options for this to control how this happens. In the application context file that defines security in Broadleaf (typically /WEB-INF/applicationContext-security.xml), we have three main beans that we need to change:
<bean id="blUserDetailsMapper" class="org.broadleafcommerce.profile.core.security.ldap.BroadleafActiveDirectoryUserDetailsMapper"> <property name="useEmailAddressAsUsername" value="true"></property> <property name="roleNameSubstitutions"> <map> <entry key="Marketing_Admin" value="ADMIN,CRM"></entry> </map> </property> </bean> <bean id="adAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider"> <constructor-arg value="mycompany.com"> <constructor-arg value="ldap://ldap.mycompany.com:389/"> <property name="userDetailsContextMapper" ref="blUserDetailsMapper"></property> <property name="useAuthenticationRequestCredentials" value="true"></property> <property name="convertSubErrorCodesToExceptions" value="true"></property> </constructor-arg></constructor-arg> </bean> <sec:authentication-manager alias="blAuthenticationManager"> <sec:authentication-provider ref="adAuthProvider"> </sec:authentication-provider></sec:authentication-manager>
First, the blUserDetailsMapper is a custom Broadleaf hook that Spring LDAP invokes to provide custom functionality to map the user details. Typically there will only be one of these types of beans configured in an application. The "useEmailAddressAsUsername" property, if set to true, tells this component to use the "mail" attribute returned from LDAP as the user name rather than the user name returned from LDAP. This may be useful if you want to use email addresses as username identifiers in Broadleaf, regardless of the Active Directory user name. If set to false, the AD username will be used as the principal. The "roleNameSubstitutions" property is a map that allows you to override Granted Authorities from LDAP with more appropriate authorities in Broadleaf. In this example, if AD returns a Granted Authority of "Marketing_Admin", then the authorities presented to Broadleaf will include "ADMIN" and "CRM". Of course, the "ADMIN" and "CRM" roles need to be configured in the "BLC_ADMIN_ROLE" table.
Next, the bean named "adAuthProvider" is a standard Spring LDAP configuration. For more information on configuring Spring LDAP, look here. This bean is an authentication provider that knows how to talk to an Active Directory server, and specifically allows you to use authentication request credentials to bind to the AD server. It also allows mapping of specific Active Directory error codes to friendlier exceptions.
Finally, the configuration for the authentication-manager sets up a Spring authentication manager, which will use the "adAuthProvider" bean to do the authentication. All other configurations are standard Spring Security configurations. Check the documentation for more information on configuring Spring Security.
The "blAuthenticationManager" is a standard Spring Security component that allows for a number of AuthenticationProviders. In this case, we specify that the *ActiveDirectoryLdapAuthenticationProvider* is the only authentication provider to use. Spring will now delegate all username and password authentication requests to this Authentication Provider.
So, to recap the process, here's how it all works:
- User browses to login page
- User submits username and password
- Spring Security delegates to the *ActiveDirectoryLdapAuthenticationProvider*
- If authentication is successful, Spring delegates to the *BroadleafActiveDirectoryUserDetailsMapper* to map the specific user details and roles from LDAP to a Broadleaf Principal
- The *AdminExternalLoginStateFilter* queries the Spring Security API, retrieves the *BroadleafExternalAuthenticationUserDetails*, creates a new Broadleaf Admin User if one doesn't exist, deletes all existing roles and permissions if one does exist, and then creates new roles and permissions based on the most recent LDAP credentials
Of course, this functionality is especially suited for use with Admin Users. Many companies will want their internal administrators' credentials stored in LDAP, and their customer profiles stored directly in Broadleaf's tables. That said, there is currently no functionality to update the LDAP server or create new users in LDAP from within Broadleaf. As for using this approach with customers, it is almost the same setup. The only changes would be that the roles would be mapped differently, and that the a different Servlet Filter would have to be created, similar to the AdminExternalLoginStateFilter, to provision a new Customer in the Broadleaf database if one didn't exist.
Because Broadleaf uses Spring Security, which is a pluggable security model, and because Broadleaf allows you to override and control the components and configurations, you can effectively use any security mechanism that you like. I've discussed using LDAP and Active Directory here. However, using a similar approach, custom security, single sign on, or almost any other authentication mechanism can be used with Broadleaf.