Prerequisite knowledge
A basic understanding of the Flex event model, Java web applications, and the Spring framework is required.
 
User level
Intermediate
Required products
Flex Builder (Download trial)
Sample files

 
Additional Requirements

 
Spring
 
Maven
 
JDK 6
Today, every application must take security into account. While security covers a wide range of areas, this article focuses on application security and more specifically, user authentication and authorization. Authentication verifies that the user is who he says he is, while authorization verifies that the current user has permission to do what he wants to do.
 
To illustrate these concepts, this article uses a sample Flex application that manages and displays lists of books. This Flex client uses BlazeDS to talk to a server that runs another application built using the Spring framework. The sample is a complete application that demonstrates the many facets of securing an application that uses Flex on the client side.
 
One of the modules the Spring framework provides is Spring Security (formerly known as Acegi). The Spring Security module takes care of the authentication as well as the authorization of remote services. I am not going to focus on the Spring integration since that has already been covered by Sébastien Arbogast in a series of articles on Adobe Developer Connection titled "The Flex, Spring, and BlazeDS full stack".
 

Attributions

The Flex, Spring, and BlazeDS full stack:
 

 
The security problem

Put simply, a secure application must first know who you are and then what you are allowed to do. Applications use authentication to ensure you are who you say you are and authorization to determine if you are allowed to do what you are trying to do. For web applications, authentication is a territory that is well known. There are many authentication methods, including username/password, iris scan, finger print, and tokens.
 
Authentication works the same in rich clients, including those built with Flex. If, however, you are creating applications that will function offline as well (for example Adobe AIR applications), there are some limitations to consider. The sample application, for instance, uses a username/password combination that is checked on the server. Therefore you cannot use the application without a network connection. Also, note that because it is a basic example, it does not use HTTPS or other forms of encryption that would normally be implemented for a secure application.
 

High level architecture of the solution

The sample application consists of two main parts, the client and the server. The architecture for the client is straightforward. It is a Flex component, deployed as a SWF file. The server component is more complex (see Figure 1).
 
Figure 1. High level overview of the architecture of a common web application. Green boxes are Spring framework modules, red are Adobe modules and the blue are custom made.
Figure 1. High level overview of the architecture of a common web application. Green boxes are Spring framework modules, red are Adobe modules and the blue are custom made.
In the server part of Figure 1, the green boxes are Spring framework modules, the red are Adobe modules, and the blue are custom, although they contain Spring functionality as well. The classes are wired using a Spring application context, transactions are configured over the service methods, method level security is configured, and so on. Because this is an article about the Flex integration, I will not elaborate on the Spring specifics. I will, however, explain the security configuration. As you can see from Figure 1, there are two places where Spring security is used. The first is intercepting web calls by filtering the URLs. The second is securing method calls. Because I expose the Spring beans using BlazeDS directly to the Flex client, this method level security is very important.
 

The sample

The sample application shows a list of books that you can filter and sort. To illustrate the use of different roles, only administrators (admins) can add new books. The sample uses Maven to build the WAR file and the Flex SWF file. For more information on how that works, see Sébastien Arbogast’s article at http://www.adobe.com/devnet/flex/articles/fullstack_pt3.html. Figure 2 shows an overview of the different components of the application.
 
Figure 2 Component diagram of the sample application. The yellow components are JAR files, the blue is a WAR file, the red is a Flex SWF file and the grey is only for configuration purposes. The WAR file is the deployable unit that will contain all other components.
Figure 2 Component diagram of the sample application. The yellow components are JAR files, the blue is a WAR file, the red is a Flex SWF file and the grey is only for configuration purposes. The WAR file is the deployable unit that will contain all other components.

 
Setting up the server component

Because Sébastien Arbogast’s series of articles explains how to configure Flex, BlazeDS, and Spring to work together, I’ll only touch on it briefly here.The first thing to configure is the web.xml file.
 
<filter> <filter-name>SpringSecurityFilterChain</filter-name> <filter-class>org.Springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>SpringSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
This part of the web.xml file shows the configuration of the Spring security filter that intercepts all URLs. The web.xml file also configures the BlazeDS service and the Spring context. Refer to the code sample for these sections.
 
The next step is to configure the URLs and the methods that are secured. The following Spring configuration is part of the Spring-security.xml file that you can also find in the sample code.
 
<security:http auto-config="true"> <security:intercept-url pattern="index.html" filters="none"/> <security:intercept-url pattern="/**/*.swf" filters="none"/> <security:intercept-url pattern="/**/*.html" access="ROLE_USER"/> <security:intercept-url pattern="/**" filters="none"/> </security:http> <security:global-method-security> <security:protect-pointcut access="ROLE_ADMIN" expression="execution(* nl.gridshore.samples.books.business.*Manager.store*(..))"/> <security:protect-pointcut access="ROLE_USER" expression="execution(* nl.gridshore.samples.books.business.*Manager.obtain*(..))"/> </security:global-method-security> <security:authentication-provider> <security:user-service> <security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN"/> <security:user name="user" password="user" authorities="ROLE_USER"/> </security:user-service> </security:authentication-provider>
The configuration shows three parts. The first part, security:http, configures the URL-based access control. Spring security has improved tremendously in version 2. Namespace support was introduced along with many sensible defaults. As you can see from the configuration (<security:intercept-url pattern="/**/*.swf" filters="none"/>), I have chosen not to secure the Flex component, so every visitor can download the component. This is the most logical way to set up the login within your Flex application.
 
The second part configures method level security. You can configure which methods to secure by using aspects and the pointcut expression language. (A detailed description of the expression language is beyond the scope of this article.) If you have a close look, you can see I have secured all methods of objects ending with “Manager” that start with “store”. Only admins can call these methods. All “obtain” methods of the same objects require the user role.
 
The third part is the authentication provider. For simplicity, I am using the provided in-memory storage. As you can see, there are two users defined, the user and the admin. Admin has two roles, while the user only has the ROLE_USER role. Only the admin can create a new book by calling one of the store methods.
 

Setting up Flex and BlazeDS

The connection between your Flex client and BlazeDS is specified in two configuration files. The first file is services-config.xml, which contains the configuration of the channels and the factories to create the proxies to the remote objects. I use the same Spring factory described in the Arbogast articles. Using this factory, we have access to the Spring framework configured beans. The services-config.xml file also includes a reference to the other config file, remoting-config.xml. This configuration file contains the destinations,or the actual remote beans, to call. The following code shows the destinations I use in the sample:
 
<destination id="bookManager"> <properties> <factory>Spring</factory> <source>bookManager</source> </properties> </destination> <destination id="authenticationHelper"> <properties> <source>nl.gridshore.samples.books.web.security.AuthenticationHelper </source> </properties> </destination>
The bookManager destination is a Spring bean that exposes the methods the user can call to obtain books and to create new books. As you can see, I use the Spring factory to have access to this destination. The authenticationHelper destination is not a Spring bean, though It does provide a hook into the framework. This bean is used to authenticate a user and obtain the roles of the authenticated user. The next block of code shows the method that accepts the username and password and then asks the authentication manager to authenticate a principal with these credentials.
 
public AuthorizationData authenticatePrincipal(String usr, String pwd) { ApplicationContext appContext =WebApplicationContextUtils.getWebApplicationContext( FlexContext.getServletConfig().getServletContext()); AuthenticationManager manager = (AuthenticationManager)appContext.getBean("_authenticationManager"); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username,password); Authentication authentication = manager.authenticate(usernamePasswordAuthenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); GrantedAuthority[] authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities(); int numAuthorities = authorities.length; String[] grantedRoles = new String[numAuthorities]; for (int counter = 0; counter < numAuthorities ; counter++) { grantedRoles[counter] = authorities[counter].getAuthority(); } String name = SecurityContextHolder.getContext().getAuthentication().getName(); return new AuthorizationData(grantedRoles,name); }
Because this is the most important part to understand of the integration with Spring security, I’m going to break up the code to explain what is happening.
 
WebApplicationContextUtils.getWebApplicationContext( FlexContext.getServletConfig().getServletContext());
Using the FlexContext class we can access the current ServletContext, which provides access to the Spring framework application context. The sample uses the namespace support of the Spring Security configuration. Using convention over configuration, an authenticationManager bean is registered in the Spring context using the name _authenticationManager.
 
AuthenticationManager manager = (AuthenticationManager)appContext.getBean("_authenticationManager");
The next step is to create an authentication token based on the provided username and password. This token is used to authenticate the principal by calling the authenticate method on the authentication manager. The returned authentication is added to the session to be remembered and used when the next call is made.
 
Authentication authentication = manager.authenticate(usernamePasswordAuthenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication);
In the next step the Flex client is provided with the roles for which the authenticated user is authorized. After obtaining the roles and the actual username, the application creates an AuthorizationData object that is then returned to the caller and to the Flex client using BlazeDS.
 
GrantedAuthority[] authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities(); int numAuthorities = authorities.length; String[] grantedRoles = new String[numAuthorities]; for (int counter = 0; counter < numAuthorities ; counter++) { grantedRoles[counter] = authorities[counter].getAuthority(); } String name = SecurityContextHolder.getContext().getAuthentication().getName(); return new AuthorizationData(grantedRoles,name);

 
Creating the remote objects

When communicating with the server, Flex makes use of the RemoteObject component. Since many things can go wrong when communicating with the server I wanted a general mechanism to deal with these problems. I created a structure of objects that use the RemoteObject component and that handle dispatched events. I use events for handling the errors and notifying the other components.
 
Figure 3 shows the classes used to create this general mechanism.
 
Figure 3. An overview of the most important classes involved in communicating with remote objects. The yellow classes are Flex framework classes, the green are custom classes, and the blue are events.
Figure 3. An overview of the most important classes involved in communicating with remote objects. The yellow classes are Flex framework classes, the green are custom classes, and the blue are events.
The RemoteService object creates a RemoteObject based on the name of the destination that is provided in the constructor. The RemoteService also registers an event listener for the FaultEvent. The following code shows the implementation of this RemoteService object:
 
package services { import mx.rpc.remoting.RemoteObject; import events.RemoteExceptionEvent; import events.AuthenticationFailureEvent; import mx.core.Application; import mx.rpc.events.FaultEvent; public class RemoteService { private static var BAD_CREDENTIALS:String = "org.Springframework.security.BadCredentialsException : Bad credentials"; protected var remoteObject:RemoteObject; public function RemoteService(id:String, destination:String) { this.remoteObject = new RemoteObject(id); this.remoteObject.destination = destination; this.remoteObject.addEventListener(FaultEvent.FAULT,onRemoteException); } public function onRemoteException(event:FaultEvent):void { if (event.fault.faultString == BAD_CREDENTIALS) { Application.application.dispatchEvent(new AuthenticationFailureEvent( AuthenticationFailureEvent.AUTHENTICATION_FAILURE,"problem while authenticating")); } else { Application.application.dispatchEvent(new RemoteExceptionEvent( RemoteExceptionEvent.REMOTE_EXCEPTION, "unknown problem occurred during a remote call : " + event.fault.message)); } } } }
The most important thing to notice in this sample code is the creation of a RemoteObject and registering the listener. Another important part is dispatching a new event using the special Application.application object.
 
The RemoteService component just makes the connection and does not really expose anything. This is taken care of by the subclasses. One of the subclasses is the SecurityService. Now that you have seen the server implementation, let’s take a look at the client side.
 
package services { import events.AuthenticationEvent; import mx.core.Application; import model.UserData; import mx.rpc.events.ResultEvent; public class SecurityService extends RemoteService{ public function SecurityService() { super("securityService","authenticationHelper"); } public function authenticatePrincipal(usr,pwd:String):void { var userData:UserData = UserData.getInstance(); userData.username = username; userData.password = password; remoteObject.authenticatePrincipal.addEventListener( ResultEvent.RESULT,handleAuthenticatePrincipal); remoteObject.authenticatePrincipal(username,password); } protected function handleAuthenticatePrincipal(event:ResultEvent):void { var userData:UserData = UserData.getInstance(); userData.authenticated = true; var obj:Object = event.result; var obtainedRoles:Array = obj.roles as Array; for(var i:int=0; i < obtainedRoles.length; i++) { userData.addGrantedRole(obtainedRoles[i]) } Application.application.dispatchEvent(new AuthenticationEvent( AuthenticationEvent.AUTHENTICATION,"user is authenticated")); } } }
This class has two functions. The first, handleAuthenticatePrincipal, is a public function that is called to authenticate a user. This function also registers a listener for a result event. The other method is called when the result event is received. The special userData object, which is a singleton, is populated with the user’s name and the roles for which he is authorized. After receiving the result event and handling the obtained data, a new event is dispatched to indicate a successful authentication.
 

 
Using the remote objects in the Flex client

Here is the last piece of the puzzle. When the user start the application, he is challenged for his username and password, authenticated, and then presented with a screen that he can use to find books. If the user is an administrator, an extra button must be visible that opens a form for creating new book entries.
 
The main component is the Application component. In the sample this is the fileMain.mxml. When the application is opened, the following function, initializeApplication, is called:
 
private function initializeApplication():void { Application.application.addEventListener( AuthenticationEvent.AUTHENTICATION, handleAuthenticationEvent); Application.application.addEventListener( RemoteExceptionEvent.REMOTE_EXCEPTION, handleRemoteExceptionEvent); authenticateUser(); } private function authenticateUser():void { showRightUIComponent('authentication'); } private function showRightUIComponent(itemToShow:String):void { switch (itemToShow) { case 'authentication': mainContentViewStack.selectedChild = authenticationForm; break; case 'allbooks' : mainContentViewStack.selectedChild = filteredBooks; break; case 'selectedbook' : mainContentViewStack.selectedChild = bookForm; break; case 'newbook' : bookForm.clearForm(); mainContentViewStack.selectedChild = bookForm; break; default: mainContentViewStack.selectedChild = welcomeMessage; } }
In this code, I first register two event listeners, one for a successful authentication event and one for a remote exception event. Then I present the user with the form shown in Figure 4. This is done using the two other functions presented in the code, authenticateUser and showRightUIComponent. The function showRightUIComponent is used to present the right item from the stack mainContentViewStack.
 
Figure 4. The sample application's login page.
Figure 4. The sample application's login page.
If the user provides the application with a valid username password combination. (username/password: user/user or admin/admin)"user", "user or admin", "admin"), the user is redirected to a page with a button to click for a list of books. If the user is an administrator, he sees a second button that can be used to create a book. When the user clicks that button, he sees a form to create a new book. The following code shows the handler for the authentication event.
 
private function handleAuthenticationEvent(event:AuthenticationEvent):void { mainNavigation.initMyComponent(); mainNavigation.addEventListener( components.MainNavigationEvent.SELECT_ITEM, handleNavigationEvent); filteredBooks.addEventListener(BookSelectedEvent.SELECT_BOOK, handleBookSelectedEvent); showRightUIComponent("default"); }
As you can see in the code I again use the function showRightUIComponent, this time to put the default item of the stack on top. I also register two event listeners, one for a navigation event and one for a book selection event. The events are dispatched when the user clicks a button and when a user selects a book from the list of books, respectively.
 
Finally, here is the code to show the Create Book button for admin users. This function is called by handleAuthenticationEvent when an authentication event is received.
 
public function initMyComponent():void { allBooksButton.visible = true; if (UserData.getInstance().isAdmin()) { newBookButton.visible = true; } }
Figure 5 shows what a normal user sees after clicking the All Books button, while Figure 6 shows the form seen by administrators after clicking the New Book button.
 
Figure 5. The screen a users sees after clicking the All Books button.
Figure 5. The screen a users sees after clicking the All Books button.
 
Figure 6. The administrator screen for adding a new book
Figure 6. The administrator screen for adding a new book.

 
Where to go from here

The sample application is not complete; you may want to modify it to take care of session ending using spring security logout mechanism. There are more errors that can occur, so improved exception handling would be another good exercise. Aside from that, start experimenting with the sample and explore the resources cited in the attributions that follow.