The following is a result of my getting familiar with Spring Security.
The example will describe how to create a simple Java helloword program which requires authentication and secures some functionality. This example will have two users jimi and bob. The program will have a function to say hello which will be available to all users. The program will have another function to say a secure hello which will only be available to administrative users. This example was built with Eclipse Galileo and Java 1.6.
- Define a new project
- Select File->New->Project
- In the New Project wizard expand the Java node and select Java Project
- Click Next
- Type HelloMain in the Project name edit box.
- Click Finish
- If prompted to open the Java perspective select Yes.
- In the Package Explorer right mouse click on the src package and select New->Package
- Name the package helloworld
- Download the helloworld.zip attachment to this page and unzip it to the HelloMain->src->helloworld folder in your workspace
- In the Package Explorer right mouse click the helloworld package and select refresh. You should now be able to expand the helloworld package and see the example classes.
- In the Package Explorer right mouse click the HelloMain folder and select New Source Folder
- Type META-INF for the folder name and click Finish
- Download the Security-Pojo-Lib-Example.zip attachment to this page and unzip it to the HelloMain->META-INF folder of your workspace.
- In the Package Explorer right mouse click the HelloMain/META-INF folder and select refresh.
- Verify that there is a lib sub-folder under the META-INF folder. If not
- Right mouse click the META-INF folder and select new->folder. Name the folder 'lib'.
- Download the following jars into the HelloMain/META-INF/lib directory. The files will have a .zip extension replace the .zip extension with .jar.
- In the Package Explorer right mouse click the lib folder and select Refresh.
- In the Package Explorer expand the lib folder and on each jar, right mouse click, select Build Path->Add to Build Path
The project should now build successfully.
To execute the sample select the Run->Run As menu item. If prompted for a run type select Java application.
When the program executes you will see some messages printed out in the console followed by a prompt for input with the text 'Enter username:'. Two values can be entered here jimi and bob. If you enter either a invalid username or invalid password a 'Bad Credentials' exception will be thrown. When the username jimi is entered along with his password of jpw, then jimi's roles are printed out followed by two hello's to jimi, the second hello is the secured method call and is prefixed with Secure. When bob, with his password of bpw, logs on his roles are printed followed by one hello to bob. Since bob is not allowed to call the secure hello method an AccessDeniedException is thrown and this is ignored by the main program.
There are 4 classes and two interfaces in the example. The interfaces identify the pluggable components of the application. The Ihelloer interface identifies two different methods which operate on a string. The second method is intended to be executed only by a user with the ADMIN role. The Helloer class implements the Ihelloer interface and the unsecured method prints 'Hello" before the string and the secured method prints "Secure Hello" before the string. The ICredentialsCollector interface identifies a method for returning a Spring Security class representing user credentials. The ConsoleCredentialCollector class implements the ICredentialsCollector interface and prompts at the console for the user to enter a username and password.
The Helloworld class contains the main entry point. The first thing it does is instantiate the Authenticator class and call it to Authenticate. Next it asks the authenticator class for the Spring context so it can obtain a bean for saying hello. After obtaining the bean to say hello it invokes both the unsecured and secured method passing them the username obtained from the authenticator. The secured call is wrapped with a try-catch to ignore an AccessDeniedException. This is the reason that you only see one message for bob.
The authenticator class contains the heart of the security processing. First it establishes the spring container. The ClassPathXmlApplicationContext will search the class path for the given xml file in order to process bean definitions. After the context is established two beans are obtained from the container. The first is a Spring Security Interface called AuthenticationProvider. This interface "is responsible for taking an Authentication request object and deciding whether or not it is valid." (Spring Security Reference) The second bean is responsible for obtaining the credentials. The next line does the authentication.
The credentials are collected via the identityChallenger bean. The example obtains the credentials from the console, but this bean could be replaced with a bean which could obtain credentials in some other fashion such as a gui prompt. The credentials are passed to the authentication provider. The provider configured here matches the collected credentials to those supplied in the spring xml file. However, the provider configuration could be changed to an LDAP authentication provider without changing the code. The authentication provider either returns a valid Authentication object or throws an exception. Next the Authenticate class populates the Security Context with the valid Authentication object. Then the username and roles are extracted from the Authentication object. The roles are printed and the username is saved in a member variable.
Authentication auth = provider.authenticate(c.collectCredential());
The ApplicationContext.xml file defines the beans for the Helloworld application. I've divided the file into two sections (see comments), Authorization and Authentication definitions. The talker bean identifies the class with the methods which will be secured. Next the autoProxyCreator bean defines an interceptor bean for the secured class. The securityInterceptor bean defines what method will be secured, what roles will be allowed to execute the method, and the beans which implement the algorithm used to check the authorization. The accessDecisionManager bean defines that the authorization will be done by the Spring Security RoleVoter class and that the voting must be unanimous. This completes the authorization definitions. The ApplicationContext.xml file included in the Security-Pojo-Lib-Example.zip file contains a simple DaoAuthenticator. The LDAP-ApplicationContext.xml file comments out the Dao authentication and supplies definitions for authenticating and determining roles against a Active Directory Domain Controller. LDAP authentication is separated into two parts authenticating the user and if the user authenticates finding their roles. The authenticationManager bean defines what authentication providers to use. In this case we are using one, the LDAP provider. The LDAP provider bean just defines the two beans to manager authenticating the user and obtaining the users roles. These are the authenticator and populator beans respectively. The last bean, initialDirContextFactory, defines the LDAP server, the DN to root search operations and credentials for doing the searches.