I just finished a task that involved using Active Directory over LDAP for authentication and authorization. I had never before worked with Spring Security and I had no idea how LDAP works. So, this made the task all the more exciting.
The task involved the following:
- Connect to active directory and search for the user.
- Authenticate the user using bind authentication.
- Load the roles for the user from active direcotry
SpringSource as with everything else, has excellent documentation for how spring security works and how to configure LDAP authentication. So, I won't go into the details here. You can read about how to use LDAP authentication with spring security over here: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ldap.html and if you're trying to integrate with most standard LDAP implementations, that's all you need.
After I went through the spring source documentation and had implemented LDAP authentication + authorization hitting the local embedded ldap server that comes with the spring security module, I started to google around for Active Directory related idiosyncrasies because I was told that there were a lot of those. Having gained a proper understanding of LDAP itself, the differences didn't seem too problematic. The only thing I had noticed is the following:
- If your users login using a user id of some sort, Active Directory stores this as the 'sAMAccountName' instead of the standard LDAP 'uid'.
- In LDAP, entries have 'distinguished names' abbreviated as 'dn'. This is analogous to a composite key in RDBMS parlance. It is made up of several other attributes like the 'uid', the 'organizational unit' abbreviated as 'ou' etc., so that the entry can be uniquely identified. In the Active Directory implementation, the counterpart of the 'uid' which is the 'sAMAccountname' is not part of the 'dn'. Instead the 'common name' abbreviated as 'cn' is. This leaves you with no option but search for the user. Otherwise, you could have used a pattern to deduce the 'dn' given the user id.
While I was googling for the Active Directory idiosyncrasies, I noticed that most of the posts were using the old bean based configuration. The new namespace based configuration makes your context/config xml file look much more readable. So, I decided to do a post.
Pointing to the Active Directory Server
The thing I'd like to mention here is, when someone needs to enable the 'searchSubtree' property for the 'user search', I think many would abandon the configuration based approach and start configuring beans for LDAP authentication. But there is a way to enable subtree search right from the URL. Refer to this if you want further information on LDAP URLs: http://en.wikipedia.org/wiki/LDAP#LDAP_URLs
Here's how you would enable subtree search for all searches right from the URL: (notice the <??sub> at the end)
The 'manager-dn' and 'manager-password' are not needed if your organization's Active Directory is set up to allow anonymous read access.
Coming to the embedded LDAP server, it is meant for unit tests and the next level integration tests. I personally didn't use it because we were going to have a Selenium test for the login page itself. But if you need it, comes with an easy configuration based solution to define and start an embedded LDAP server that reads the content from an 'ldif' file.
Configuring the LDAP Authentication Provider
Here's my simple ldap authentication provider configuration:
You can further improve your search performance by tuning the search filters. For example you can use something like this: (Note that you have to escape the 'ampersand - &' symbol used in the LDAP search filter syntax.)
This is a very succinct page on the LDAP search filter syntax: http://msdn.microsoft.com/en-us/library/aa746475(VS.85).aspx
And finally, a tool that I found very useful while trying to understand what things look like inside the Active Directory server was Microsoft's Active Directory Explorer' which can be downloaded for free from here: http://technet.microsoft.com/en-us/sysinternals/bb963907.aspx
A Note on Escaping Comma Characters
This issue took up a lot of debugging time for me and I thought I should mention this.
In LDAP syntax, the comma (,) character is used to separate the elements in distinguished names. So if you have a comma in your test, you'll have to escape it with a backslash (\) like this:
cn=Ratnagiri\, Vijay,ou=My OU,ou=Another OU,dc=mycompany,dc=com
Now, when you're specifying this in your XML config file, you might be tempted to escape the backslash. But you shouldn't.
And now, if you're extracting the 'dn' into a properties file, you might fall into another trap! The backslash is a character used for escaping in the properties file syntax as well and when the value of a property is being read from a properties file as a string, if a backslash precedes a character that doesn't need to be escaped, the backslash is just dropped. So, in a properties file:
#WRONG: This will be read as cn=Ratnagiri, Vijay,ou=My OU,ou=Another OU,dc=mycompany,dc=com and this will cause a problem when it is passed on to the LDAP realated code. manager.dn=cn=Ratnagiri\, Vijay,ou=My OU,ou=Another OU,dc=mycompany,dc=com #RIGHT: This will be read as cn=Ratnagiri\, Vijay,ou=My OU,ou=Another OU,dc=mycompany,dc=com manager.dn=cn=Ratnagiri\\, Vijay,ou=My OU,ou=Another OU,dc=mycompany,dc=com
This article was written for:
- Spring Security 3.0.2.RELEASE