BlazeDS Developer Guide

Custom authentication

View comments | RSS feed

For custom authentication, the client application passes credentials to the server without relying on the browser. Although you apply security constraints to a destination, you actually log in and log out of the channels associated with the destination. Therefore, to send authentication credentials to a destination that uses custom authentication, you specify a user name and password as arguments to the ChannelSet.login() method. You remove credentials by calling the ChannelSet.logout() method.

Note: The ChannnelSet.login() and ChannelSet.logout() methods are the preferred methods for setting and removing credentials. You can also send credentials to a destination by calling the setCredentials() method of a component such as RemoteObject, Producer, Consumer, WebService, or HTTPService. However, the setCredentials() method does not actually pass the credentials to the server until the first attempt by the component to connect to the server. Therefore, if the component issues a fault event, you can not be certain whether the fault happened because of an authentication error, or for another reason. The ChannelSet.login() method connects to the server when you call it so that you can handle an authentication issue immediately. Similarly, you can remove credentials from a component, such as RemoteObject, Producer, Consumer, WebService, or HTTPService, with the component’s logout() method. However, this method only sends a logout request to the server if the client is connected and authenticated. If these conditions are not met, the behavior for this method is to do nothing other than clear any credentials that have been cached for use in automatic reconnects. The ChannelSet.logout() method removes credentials immediately.

Because multiple destinations can use the same channels, and corresponding ChannelSet object, logging in to one destination logs the user in to any other destination that uses the same channel or channels. If two components apply different credentials to the same ChannelSet object, the last credentials applied are used. If multiple components use the same authenticated ChannelSet object, calling the logout() method logs all components out of the destinations.

The login() and logout() methods return an AsyncToken object. Assign event handlers to the AsyncToken object for the result event to handle a successful call, and for the fault event to handle a failure.

How the server processes the logout() method depends on the setting of the per-client-authentication property:

  • If the per-client-authentication property is false (default), the logout() method invalidates the current session. A new FlexSession object is automatically created to replace it, and the new session does not contain any attributes or an authenticated information. If authentication information is stored at the session level, and a client disconnect results in the FlexSession being invalidated, authenticated information is also cleared.
  • If the per-client-authentication property is true, the logout() method clears the authentication information stored in the FlexClient object, but does not invalidate the FlexClient object or FlexSession object.

Note: Calling the login(), setCredentials(), or setRemoteCredentials() method has no effect when the useProxy property of a component is set to false.

Custom authentication example

The following example uses the ChannelSet.login() and ChannelSet.logout() methods with a RemoteObject control. This application performs the following actions:

  • Creates a ChannelSet object in the creationComplete handler that represents the channels used by the RemoteObject component.
  • Passes credentials to the server by calling the ROLogin() function in response to a Button click event.
  • Uses the RemoteObject component to send a String to the server in response to a Button click event. The server returns the same String back to the RemoteObject component.
  • Uses the result event of the RemoteObject component to display the String in a TextArea control.
  • Logs out of the server by calling the ROLogout() function in response to a Button click event.
<?xml version="1.0"?>
<!-- security/SecurityConstraintCustom.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%"
    creationComplete="creationCompleteHandler();">

    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.messaging.config.ServerConfig;
            import mx.rpc.AsyncToken;
            import mx.rpc.AsyncResponder;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            import mx.messaging.ChannelSet;

            // Define a ChannelSet object.
            public var cs:ChannelSet;
            
            // Define an AsyncToken object.
            public var token:AsyncToken;

            // Initialize ChannelSet object based on the 
            // destination of the RemoteObject component.
            private function creationCompleteHandler():void {
                if (cs == null)
                    cs = ServerConfig.getChannelSet(remoteObject.destination);                    
            }

            // Login and handle authentication success or failure. 
            private function ROLogin():void {
                // Make sure that the user is not already logged in.
                if (cs.authenticated == false) {
                    token = cs.login("sampleuser", "samplepassword");
                    // Add result and fault handlers.
                    token.addResponder(new AsyncResponder(LoginResultEvent, LoginFaultEvent));
                }
            }

            // Handle successful login.
            private function LoginResultEvent(event:ResultEvent, token:Object=null):void  {
                switch(event.result) {
                    case "success":
                        authenticatedCB.selected = true;
                    break;
                    default:
                }
            }
            
            // Handle login failure.
            private function LoginFaultEvent(event:FaultEvent, token:Object=null):void {
                switch(event.fault.faultCode) {
                    case "Client.Authentication":
                        default:
                            authenticatedCB.selected = false;
                            Alert.show("Login failure: " + event.fault.faultString);
                }
            }

            // Logout and handle success or failure.
            private function ROLogout():void {
                // Add result and fault handlers.
                token = cs.logout();
                token.addResponder(new AsyncResponder(LogoutResultEvent,LogoutFaultEvent));
            }

            // Handle successful logout.
            private function LogoutResultEvent(event:ResultEvent, token:Object=null):void {
                switch (event.result) {
                    case "success":
                        authenticatedCB.selected = false;
                        break;
                    default:
                }
            }

            // Handle logout failure.
            private function LogoutFaultEvent(event:FaultEvent, token:Object=null):void {
                Alert.show("Logout failure: " + event.fault.faultString);
            }

            // Handle message recevied by RemoteObject component.
            private function resultHandler(event:ResultEvent):void {
                ta.text += "Server responded: "+ event.result + "\n";
            }

            // Handle fault from RemoteObject component.
            private function faultHandler(event:FaultEvent):void {
                ta.text += "Received fault: " + event.fault + "\n";
            }
        ]]>
    </mx:Script>

    <mx:HBox>
        <mx:Label text="Enter a text for the server to echo"/>
        <mx:TextInput id="ti" text="Hello World!"/>
        <mx:Button label="Login" 
            click="ROLogin();"/>
        <mx:Button label="Echo"  
            enabled="{authenticatedCB.selected}"
            click="remoteObject.echo(ti.text);"/>
        <mx:Button label="Logout" 
            click="ROLogout();"/>
        <mx:CheckBox id="authenticatedCB" 
            label="Authenticated?" 
            enabled="false"/>
    </mx:HBox>
    <mx:TextArea id="ta" width="100%" height="100%"/>

    <mx:RemoteObject id="remoteObject"
        destination="remoting_AMF_SecurityConstraint_Custom"
        result="resultHandler(event);"
        fault="faultHandler(event);"/>
</mx:Application>

Configure Tomcat for custom authentication

Perform the following configuration steps to use custom authentication with Tomcat:

  1. Place the flex-tomcat-common.jar file in the tomcat/lib/blazeds directory.
  2. Place the flex-tomcat-server.jar file in the tomcat/lib/blazeds directory.
  3. Edit the tomcat/conf/catalina.properties file to add the following path to the common.loader property:
    ${catalina.home}/lib/blazeds/*.jar
    
    
  4. Edit the tomcat/conf/context.xml file to add the following tag to the Context descriptors:
    <Valve className="flex.messaging.security.TomcatValve"/> 
    
    
  5. Restart Tomcat.

You can now authenticate against the current Tomcat realm. Typically user information is in the conf/tomcat-users.xml file. For more information, see the Tomcat documentation.


Comments


No screen name said on Sep 28, 2009 at 4:31 PM :
I think the example is incomplete do to the fact that the mx:RemoteObject's destination on how it is configured is missing.
mpeterson said on Sep 29, 2009 at 5:06 AM :
The previous pages in the doc show how to set up a security constraint in services-config.xml and how to apply it to a destination.
No screen name said on Sep 29, 2009 at 7:57 AM :
You are correct in the fact that there are examples of how to apply a security-constraint to destinations. The examples that are previous all use basic authentication and rely on the basic authentication of the container to work. I am trying to configure a Custom authentication as this page shows. In this page you have a creationCompleteHandler() method that is called which instantiates a ChannelSet object based on the RemoteObjects destination.

cs = ServerConfig.getChannelSet(remoteObject.destination);

I would assume that the destination would be configured in the services-config.xml file under the <channels> tag element. In the example you find the RemoteObjects destination is identified as 'remoting_AMF_SecurityConstraint_Custom' but what I do not find is anywhere in the documentation that shows how to use this destination as a <channel-definition> that can be applied to the 'cs' object in the example.

So what I am asking is there any better documentation that shows and explains the full setup of a Custom authentication and authorization for JEE containers?
mpeterson said on Sep 29, 2009 at 9:04 AM :
Very good point. The destination would look very similar to the basic auth example. If the security-constraint is defined in-line in the destination, it would look something like this:<destination id="'remoting_AMF_SecurityConstraint_Custom">
...
<security>
<security-constraint>
<auth-method>Custom</auth-method>
<roles>
<role>TestRole</role>
</roles>
</security-constraint>
</security>
</destination>
No screen name said on Sep 29, 2009 at 3:14 PM :
Utilizing the Custom login constraint basically ties the security into the already defined security of the application container. What was happening is that when calling login the Custom login uses wrappers classes on the container side. Since I did not have a JBoss login-config.xml defined for the default, in JBoss AS 5.1 its the 'other' policy, nothing was happening. Once I configured the 'other' policy to utilize the DatabaseServerLoginModule everything started working. So in this instance Custom login really means container login security integration not Custom developed login.

 

RSS feed | Send me an e-mail when comments are added to this page | Comment Report

Current page: http://livedocs.adobe.com/blazeds/1/blazeds_devguide/services_security_5.html