flexlogo.png

In my previous blog post : integrating flex 3 with spring security I made a good effort to create a nice flex 3 application and integrate authentication and authorization with spring security. A few days a go I received a trackback from sven. Curious as I am I started reading the material he provided and especially the other links he mentioned. That made me think about my own solution. To be honest I think I did not really do a good job. It works, but still not optimal for most of the flex situations.

In my previous post I already mentioned the problem of sessions that are closed and exception handling with respect to security. In this article I am looking at the available mechanisms for security in flex. In this post I explain why I am not really using the flex or better BlazeDS security mechanisms and what you probably should use them for.

If this made you curious enough, read on. If you have questions, remarks or improvements, do not hesitate to use the comments feature of this blog item.

The security problem

The basic problem: we want to know who you are and we then what you are allowed to do. In other words, we want to use authentication to determine if you are who you say you are and authorization to determine what you are allowed to do. For web applications, authentication is a territory that is well known. There are lot’s of different ways to determine if you are who you say you are. Think about password, iris scan, finger print, tokens, etc.

When clients are becoming richer or fat, this does not have to change, but if you are creating applications that should function offline as well, you have some limitations. For my sample I am using a username/password combination. I am not using https, not using encryption. It is a basic sample.

I am going to show you a possible solution that uses some basic components to authenticate against a server side web application. Using BlazeDS we are talking direct to spring beans that are secured using spring security.

Flex and BlazeDS security mechanism for remoting

BlazeDS has a few things that can help you to secure the remote calls from the web client. You can configure security for remote destinations. This way BlazeDS makes sure you have been authenticated and have the required role. The following piece of code gives an example of a destination that is secured. The destination references a security-constraint that is also presented in the code.

<destination id="bookManager">
    <properties>
        <factory>spring</factory>
        <source>bookManager</source>
    </properties>
    <security>
        <security-constraint ref="trusted"/>
    </security>
    <security-constraint id="trusted">
        <auth-method>Custom</auth-method>
        <roles>
            <role>ROLE_USER</role>
        </roles>
    </security>
</destination>

This way the user is asked for a username/password using for instance using Basic authentication. This mechanism makes use of the application server. Since not all application servers use the same mechanism for authentication there are special implementations available for a number of application servers. If you want to use an application level authentication you need to create a custom LoginCommand.

At first I thought this was the way to go, to be honest I never really understood it. So maybe I am missing something. For now I think my solutions is more flexible and easier to understand for an average java programmer. Therefore the flex/BlazeDS model was not the way to go for me. Let’s go over to a general view of my solution.

Overview of my solution

I am a java programmer coming to the flex world. Therefore my solution might be a bit to java-ish. Since I am integrating with java and spring security specifically, I do not really mind. The following things were important to me when creating the solution:

  • No double configuration of roles for authorization, but the flex application must have the availability of obtained authorizations.
  • Easy recovery in case of exception due to bad credentials, unauthorized access of remote methods, session time out, etc.
  • No necessity for offline working (no authentication details available in the client)
  • Possibility of re-authentication in case of a session time out without asking for credentials again.

This all can be done using some hierarchy in classes, use of singletons for user data and custom events. The following image gives a schematic overview of the solution. You are missing the actual pages: Home and AuthenticationForm. You are also missing the actual service implementations on the server side. Both topics will be talked about later on.

Remote services, error handling and events.

FlexDiagrams.jpg

The image shows the main components for authentication and authorization from the flex side. The yellow classes are provided by the framework, the green ones are services that interact with remote services and the data obtained from the server. The blue ones are the events used by the security mechanism to expose the results of authentication and authorization requests.

Let’s start with the services. The super class RemoteService contains some generic methods for catching and translating them into events. The constructor excepts an id and the name of the destination for the RemoteObject to create. Beware that this name should be the same as configured in the destination of the remoting-config.xml. The next block of code shows you the implementation of this RemoteService

/**
 * Super class for all remote services that contains some generic methods.
 */
public class RemoteService {
    private static var BAD_CREDENTIALS:String = 
            "org.springframework.security.BadCredentialsException : Bad credentials";
    protected var remoteObject:RemoteObject;

    /**
     * Constructor accepting an id and destination for the actual RemoteObject to create. An event listener 
     * is added for exceptions.
     * @param id String representing the id of the new RemoteObject to create
     * @param destination String representing the destination of the RemoteObject to create
     */
    public function RemoteService(id:String, destination:String) {
        this.remoteObject = new RemoteObject(id);
        this.remoteObject.destination = destination;
        this.remoteObject.addEventListener(FaultEvent.FAULT,onRemoteException);
    }

    /**
     * generic fault event handler for all remote object actions. based on the received message an action 
     * is taken, mostly throwing a new event.
     * @param event FaultEvent received for handling
     */
    public function onRemoteException(event:FaultEvent):void {
        trace('code : ' + event.fault.faultCode + 
              ', message : ' + event.fault.faultString + 
              ',detail : ' + event.fault.faultDetail);
          
        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 onRemoteException is not finished yet, at the moment we only handle BAD_CREDENTIALS differently than other exceptions. I can imagine that we will have more exceptions here. But for now the structure is clear. Most important to notice is the use of events. Using the special Application object, we dispatch the AuthenticationFailureEvent as well as the RemoteExceptionEvent. The next thing is to create a sub-class of this service that actually uses the connection. So let’s have a look at the SecurityService

The SecurityService is used to authenticate a user making use of the provided username and password. The following block of code contains the implementation if this method:

public function authenticatePrincipal(username:String,password:String):void {
    var userData:UserData = UserData.getInstance();
    userData.username = username;
    userData.password = password;
    remoteObject.authenticatePrincipal.addEventListener(ResultEvent.RESULT,handleAuthenticatePrincipal);
    remoteObject.authenticatePrincipal(username,password);
}

Important in this piece of code is obtaining the singleton UserData instance. This instance contains the credentials and has some utility methods to see if the logged in user has administrative rights or is a plain user. The way to get the results back looks a bit strange to the average java programmer, but we are doing a-synchronous calls (like JMS guys). The next piece of code gets called if the response is received and handles this response.

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&#91;i&#93;)
    }

    Application.application.dispatchEvent(
            new AuthenticationEvent(AuthenticationEvent.AUTHENTICATION,"user is authenticated"));
};
&#91;/sourcecode&#93;
<p>As you can see in the code we tell the userData object that he is authenticated now and we add the roles he is authorized for. To make sure that those that are interested get notified of the authentication we throw an event.
<p>The next step is to actually start the authentication process. This is done on the visual components. The first one that gets loaded is the <strong>Home.mxml</strong> page.</p>

<h3>Visible components interacting with services</h3>
<p>To demonstrate the complete authentication process we need only two visual components: <strong>Home.mxml</strong> and <strong>AuthenticationForm.mxml</strong>. I am not going to show them completely, you can browse the sources only. (see references at the bottom). In Home.mxml we add listeners for the <strong>AuthenticationEvent</strong> as well as the <strong>RemoteExceptionEvent</strong>, then we create the form and add it to the main content panel. The following piece of code shows just that:</p>

private function initializeApplication():void {
    Application.application.addEventListener(AuthenticationEvent.AUTHENTICATION, handleAuthenticationEvent);
    Application.application.addEventListener(RemoteExceptionEvent.REMOTE_EXCEPTION, handleRemoteExceptionEvent);
    authenticateUser();
}

private function authenticateUser():void {
    myMainContentPanel.removeAllChildren();
    var authenticationForm:AuthenticationForm = new AuthenticationForm();
    authenticationForm.securityService = authenticationHelper;
    myMainContentPanel.addChild(authenticationForm);
}

As you can see in the code we call the method handleAuthenticationEvent when a user gets authenticated. This method is not interesting for our cause, it could just show Yes, I am authenticated in an alert box. Let’s have a look at the AuthenticationForm now. This is also a pretty simple class with a call to the security service and a method that handles the a-synchronous callback.

private function submitLogin():void {
    submitButton.enabled = false;
    Application.application.addEventListener(
            AuthenticationFailureEvent.AUTHENTICATION_FAILURE,loginServiceFaultHandler);
    securityService.authenticatePrincipal(userName.text,password.text);
}

private function loginServiceFaultHandler(event:AuthenticationFailureEvent):void {
    authenticationMessage.text = "Problem while authentication ...";
    authenticationMessage.visible = true;
    submitButton.enabled = true;
}

As you can see, the AuthenticationFailureEvent is handled by this form page. We show just a text that there was something wrong. In any other cases we let the Home page catch the AuthenticationEvent, we do nothing here.

Final part to cover is the server side implementation. That is what we’ll do next.

Server side implementations of remote services

Within the remoting-config.xml I have configured the following destination:

<destination id="authenticationHelper">
    <properties>
               <source>nl.gridshore.samples.books.web.security.security.AuthenticationHelper</source>
    </properties>
</destination>

Within the class Authenticationhelper you can find the following method that obtains the spring application context, executes the authentication and handles the obtained principal. Take notice of the name of the bean we obtain. This is the default name when using the namespace configuration of spring-security. The other important part is the creation of the UsernamePasswordAuthenticationToken and doing the actual authentication. Problems with authentication result in a runtime exception that is propagated into flex and handled by the RemoteService object.

public AuthorizationData authenticatePrincipal(String username, String password) {
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(
flex.messaging.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); } [/sourcecode]

That is about it, if you feel this is the way to go, check the sources in my google code repo. If you have strong opinion that this is just wrong, please share your thoughts.

References

Integration spring security (Acegi) and flex 3 the sequel
Tagged on:             

21 thoughts on “Integration spring security (Acegi) and flex 3 the sequel

  • November 27, 2009 at 4:21 am
    Permalink

    AuthenticationManager manager = (AuthenticationManager)appContext.getBean(“_authenticationManager”);
    how did you get this bean?

  • February 16, 2009 at 6:46 am
    Permalink

    Hello jettro,

    I already made some modification in my code. and the result was awesome. when the user already login, i redirect it to welcome screen. I just need to prevent the user overriding current user session when logging in different user in the same browser/tab in firefox. Or the must logout before the other user can login 🙂

    Thanks a lot.
    Cheers.

    • February 14, 2009 at 9:21 am
      Permalink

      I have written a reply on the forum, I am curious enough to do some experiments myself as well. If I find something I’ll post it.

      thanks, Jettro

  • September 3, 2008 at 8:39 pm
    Permalink

    sorry for my mistake, you’re right the remoteService is from BlazeDS but my customer want XML over Http and i don’t know if RemoteService (blazeDS) do that.
    I developed a java service which send back xml stream. i catch now the response in the event.result with all details (status code, error message, …)

    It’s little heavy as a workaround, i hope that adobe will evolve httpservice to include such feature

    Thks jettro for your help

  • September 3, 2008 at 7:12 pm
    Permalink

    Maybe you can create a separate 500 page, but I am not sure. The RemoteService is from BlazeDS and free to use.

  • September 3, 2008 at 6:44 pm
    Permalink

    Thks for your answer, but i don’t use Remote Object which i thing belongs to Flex Data Service (not free). I’m only using HttpService, and what i get in my fault message is only a flex error (http call error … 2023) and not the functional message sent by the server side.

    Maybe i should develop a server side service to send back as http response an xml flow describing the error. and then set the http status to 200, and get the resultHandler process the response ?

  • September 3, 2008 at 6:30 pm
    Permalink

    Hi Cainmaro,
    in my sample I have created a super class for remote sevices with the following code:

        public class RemoteService {
            private static var BAD_CREDENTIALS:String =
                    "org.springframework.security.BadCredentialsException : Bad credentials";
    
            protected var remoteObject:RemoteObject;
    
            /**
             * Constructor accepting an id and destination for the actual RemoteObject to create. An event listener
             * is added for exceptions.
             * @param id String representing the id of the new RemoteObject to create
             * @param destination String representing the destination of the RemoteObject to create
             */
            public function RemoteService(id:String, destination:String) {
                this.remoteObject = new RemoteObject(id);
                this.remoteObject.destination = destination;
                this.remoteObject.addEventListener(FaultEvent.FAULT,onRemoteException);
            }
    
            /**
             * generic fault event handler for all remote object actions. based on the received message an action
             * is taken, mostly throwing a new event.
             * @param event FaultEvent received for handling
             */
            public function onRemoteException(event:FaultEvent):void {
                trace('code : ' + event.fault.faultCode +
                      ', message : ' + event.fault.faultString +
                      ',detail : ' + event.fault.faultDetail);
    
                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));
                }
            }
        }
    

    Not sure if that is what you mean, but you catch the fault event and can use the message to differentiate on the type of error

  • September 2, 2008 at 4:14 pm
    Permalink

    Hi,

    Is there a way to get better error reporting using HTTPService? faultEvent didn’t contain any infos !! We cant listen for flash.events.HTTPStatusEvent.HTTP_STATUS, and i want to get a clear error for my end users (from flex, i want to access exception informations i caught in the java side)

    Thks for your help

  • August 27, 2008 at 11:37 am
    Permalink

    I agree that you would expect spring security to ask for credentials before actually giving you the flex application. There a few things that you might try:
    1. Check your web.xml filter settings that everything is included in the spring security filter “*”
    2. remove the http basic, you will be presented a default form
    3. put log to debug for spring security and check it thorough, you can send me the log by mail if you want to (I send you a mail already so you can reply to that)

  • August 27, 2008 at 11:24 am
    Permalink

    here is the result of remplacing with ]

    [http]
    [intercept-url pattern=”pilot.html” access=”ROLE_ADMING,ROLE_AGENT” /]
    [intercept-url pattern=”/pilot.swf” access=”ROLE_ADMING,ROLE_AGENT” /]
    [intercept-url pattern=”/services/**” access=”ROLE_ADMING,ROLE_AGENT” /]
    [http-basic/]
    [/http]

  • August 27, 2008 at 11:09 am
    Permalink

    Another try to post my source code (hope it works)

    
        	
        	
        	
    		    	    	   	
    
    

    Thks for your help

  • August 27, 2008 at 10:55 am
    Permalink

    Sorry, I am having some problems with html or tags in the comments, can you replace the < with a [

  • August 27, 2008 at 10:18 am
    Permalink

    Sorry, my tag doesn’t work to show you my code.
    Which tag do you use ?

    Thks

  • August 27, 2008 at 10:16 am
    Permalink

    Thanks jettro,

    here is my spring-security.xml :

    All call to my java services (direct call to server side with my browser) work fine. each time the browser display the authentication window and after loggin in, i get the result.

    Thks

  • August 26, 2008 at 9:48 pm
    Permalink

    Sorry, but you need to use special html codes for tags, therefore I cannot see the code you entered. I do have a feeling this could be the cause of the anonymous access of your application. Spring security adds an anonymous user credential to the session when you first come to the remote service. Did you use filter=none for the swf file? and for all other static resources? It could be you can find your solution in that direction.

    hope that helps

  • August 25, 2008 at 9:19 pm
    Permalink

    Hi,

    I really appreciate your sample and explanation it was very helpful

    I have one question if you could help me. I’m using spring http basic authentication to authenticate users of my flex application. (without BlazeDS, and with only flex httpservice)
    I used in the flex side a service like the security service you showed to get authorizations data (using httpservice) and it works fine. I use this authorization data to initialize my menuBar (menu items depending on user profile).
    When i start my application, at the first time i get an error due to my httpservice call (call of my java authorization service) before entering any authentication credentiels. (the authentication window appears at the same time as a flex alert about http call).
    I configured my spring-security.xml like this :

    So what I expect is that spring ask me first for authentication before downloading/executing any flex code !!

    I guess that the first call of my authorization httpservice is executed before the creation of any user session in the srping security side.

    (After the first execution, when i refresh my browser, yne menuBar is displayed correctly, because the session is already opened, so my authorization service retreive authorization data successfuly

    Any idea could be very helpful

    Thanks in advance

    Cainmaro

  • July 24, 2008 at 9:46 am
    Permalink

    Hi Jan,
    I had/have some problems with sessions as well. But it seems my sample is working fin now. I have done a test with a test.html and the following configuration of spring:
    <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>
    From the log you can see that it is working:

    09:41:33,369 DEBUG DefaultFilterInvocationDefinitionSource:196 – Converted URL to lowercase, from: ‘/test.html’; to: ‘/test.html’
    09:41:33,370 DEBUG DefaultFilterInvocationDefinitionSource:224 – Candidate is: ‘/test.html’; pattern is /**/*.html; matched=true
    09:41:33,371 DEBUG AbstractSecurityInterceptor:250 – Secure object: FilterInvocation: URL: /test.html; ConfigAttributes: [ROLE_USER]
    09:41:33,371 DEBUG AbstractSecurityInterceptor:313 – Previously Authenticated: org.springframework.security.providers.UsernamePasswordAuthenticationToken@a9b69cfb: Principal: org.springframework.security.userdetails.User@ed9d7500: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Password: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
    09:41:33,371 DEBUG AbstractSecurityInterceptor:273 – Authorization successful
    09:41:33,372 DEBUG XmlWebApplicationContext:273 – Publishing event in context [org.springframework.web.context.support.XmlWebApplicationContext@2f74a57e]: org.springframework.security.event.authorization.AuthorizedEvent[source=FilterInvocation: URL: /test.html]
    09:41:33,373 DEBUG AbstractSecurityInterceptor:284 – RunAsManager did not change Authentication object
    09:41:33,375 DEBUG FilterChainProxy:355 – /test.html reached end of additional filter chain; proceeding with original chain
    09:41:33,377 DEBUG ExceptionTranslationFilter:104 – Chain processed normally
    09:41:33,378 DEBUG HttpSessionContextIntegrationFilter:255 – SecurityContextHolder now cleared, as request processing completed

  • July 23, 2008 at 8:44 pm
    Permalink

    With great interest I’ve read this blog entry. Liked it a lot! Now here’s a question. I was under the impression that by using Spring security this way, and filling out proper authentication server (HTTP) session gets the appropriate information. What I wanted to check whether I could use Spring security to shield a Java Servlet deployed as part of the Java/JEE backend services. When I first enter the credentials in the Flex app – which calls out to the SecurityService and after that try to access the Java servlet – the servlet challenges me for a username/password. I did not expect that, what am I missing?

    This is what I have in the spring-security.xml:

    <security:intercept-url pattern="/TestServlet" access="ROLE_ADMIN"/>

    So – even after initial authentication using ‘admin/admin’ in the Flex app, I still get challenged when I access the ‘/TestServlet.

  • Pingback:Gridshore » Blog Archive » Integrating flex 3 with spring security (formerly known as Acegi)

Comments are closed.