Package org.springframework.richclient.security

Integrates Acegi Security System for Spring into RCP.

See:
          Description

Interface Summary
ApplicationSecurityManager This interface defines the operations required of an Application Security Manager for the RCP framework.
AuthenticationAware A Spring managed bean implementing this interface will be automatically notified of any change in the user's Authentication token.
LoginAware A Spring managed bean implementing this interface will be automatically notified of any user login or logout activities.
SecurityController A security controller is responsible for authorizing other Authorizable objects.
SecurityControllerManager A SecurityControllerManager is responsible for linking a controllable object, one that implements SecurityControllable, to the appropriate SecurityController to manage it.
 

Class Summary
AuthenticationEvent Event fired when the user's authentication token changes.
AuthenticationFailedEvent Event fired when an authentication attempt fails.
ClientSecurityEvent Parent for all RCP security related application events.
LoginCommand Provides a login interface to the user.
LoginDetails This class provides a bean suitable for use in a login form, providing properties for storing the user name and password.
LoginEvent Event fired when a user logs in.
LoginForm This class provides a simple form for capturing a username and password from the user.
LogoutCommand Provides a command to log the current user out.
LogoutEvent Event fired when a user logs out.
RemotingSecurityConfigurer Correctly configures the username and password on Spring's remoting proxy factory beans.
SecurityAwareConfigurer This class performs two main functions: It is a bean post-processor that will set the current authentication token on any newly created beans that implement AuthenticationAware. It listens for application ClientSecurityEvents and updates all the beans in the context that implement either AuthenticationAware or LoginAware according to the event received. In order for all this to take place, a singleton, non-lazy instance of this class must be defined in the Spring ApplicationContext.
SessionDetails Deprecated. by the creation of new ApplicationSecurityManager
 

Package org.springframework.richclient.security Description

Integrates Acegi Security System for Spring into RCP.

Overview

Acegi Security is a comprehensive open-source security system that delivers fully-featured security to Spring applications. To learn more about Acegi Security or access detailed documentation, please visit the project home page at http://acegisecurity.sourceforge.net.

It is envisaged that many RCP clients will be connecting with a remote Spring-powered server. In such deployments, security becomes of paramount importance. Whilst transport-layer security (such as HTTPS, port filtering and firewalls) are essential to almost all production applications, this package delivers comprehensive application-layer security to RCP clients by hooking into the Acegi Security project.

Background Knowledge

Whilst you should really read the Acegi Security System for Spring reference documentation to fully understand the architecture, the most important details you need to understand in order to utilize this RCP package is the summarized below.

RCP uses the following key Acegi Security classes and interfaces:

ContextHolder, which simply uses a ThreadLocal to store a SecureContext implementation.

Authentication, which stores the details of a principal, credentials and its granted authorities. RCP uses Acegi Securit's UsernamePasswordAuthenticationToken, which simply represents a username and password for the principal and credentials respectively.

AuthenticationManager, which is able to accept a request Authentication object (containing only the principal and credentials details), process its validity, and return a populated Authentication object (also containing the GrantedAuthorty[]s).

Per-thread vs. per-Application authentication

One thing to keep in mind is that Acegi maintains authentication credentials on a per-thread basis (ContextHolder described above). This makes sense when considering server side implementations where different threads are working on behalf of potentially different principals. In a rich application, however, it is rare to have anything but a "global" notion of the logged in user (and associated credentials). In fact, having a per-thread credential store is problematic for a rich application where operations may take place on different threads (a main thread, UI event dispatch thread, worker threads, etc.). Further, it would be difficult to propogate credentials to all new threads created, or to update existing threads when a user changes credentials (such as loggin out or logging in as a different user).

For these reasons, the application security model provided within RCP uses a global store for credentials. See the ApplicationSecurityManager below.

Major Players - Objects, Interfaces, and Events

ApplicationSecurityManager and DefaultApplicationSecurityManager

Instances of ApplicationSecurityManager are responsible for performing the security operations of user login and logout and maintaining the global authentication token for the user. A default implementation is provided in DefaultApplicationSecurityManager.

An instance of the ApplicationSecurityManager is available from ApplicationServices like this: Application.services().getApplicationSecurityManager(). Application code can access the current authentication token by calling the getAuthentication() method (or it can implement the notification interfaces defined below).

As the ApplicationSecurityManager handles login/logout requests, it fires a set of events to inform the application of the security lifecycle. The table below shows the events that are fired in response to various login and logout processing.

ActionEvents Fired
Successful loginAuthenticationEvent, LoginEvent
Failed loginAuthenticationFailedEvent
LogoutAuthenticationEvent, LogoutEvent

In order to perform authentication operations, the ApplicationSecurityManager must have an AuthenticationManager configured. This can be done in the application context, like this:

 
    <bean id="applicationSecurityManager"
          class="org.springframework.richclient.security.support.DefaultApplicationSecurityManager">
          <property name="authenticationManager" ref="authenticationManager"/>
    </bean>
 
 

LoginCommand and LogoutCommand

LoginCommand is a simple implementation of an ActionCommand that shows a dialog for collecting a user name and password and then handing them off to the ApplicationSecurityManager to perform the actual login processing (see below for more details). LoginCommand makes some very simple assumptions on the vaildation constraints for the username and password fields, so you might need to subclass it and provide your own implementation of the login form.

LoginCommand implements a simplistic login failure handling scheme - it lets the user keep trying as long as they want. Again, you'll probably want to subclass to provide something more clever. Future work will hopefully include a pluggable login failure handling strategy. One final configurable element on the LoginCommand is how the login dialog should respond to the user pressing the Cancel button. This handling is controlled by the closeOnCancel property on LoginCommand, which defaults to true. If closeOnCancel is true and the user cancels the dialog, then the applicaiton will be closed, by calling getApplication().close()

LogoutCommand is an implementation of ActionCommand that simply invokes the logout processing in the ApplicationSecurityManager.

AuthenticationAware

AuthenticationAware is a tag interface that marks beans in the application context. Any bean that implements this interface will be initially notified of the current authentication token (during bean post-processing) and subsequently notified whenever the authentication token changes. See SecurityAwareConfigurer for more details.

LoginAware

LoginAware is a tag interface that marks beans in the application context. Any bean that implements this interface will be notified of two major security events: login and logout. See SecurityAwareConfigurer for more details.

SecurityAwareConfigurer

SecurityAwareConfigurer is a key player in the security architecture. It is both a BeanPostProcessor and an ApplicationListener. Its job is to handle beans that implement AuthenticationAware and LoginAware and configure them with authenticaiton information and notify them of key security events.

As a bean post-processor, SecurityAwareConfigurer handles any bean that implements AuthenticationAware and configures them with the current authentication token.

As an ApplicationListener, SecurityAwareConfigurer watches for ClientSecurityEvents and turns them into method notifications on the AuthenticationAware and LoginAware interfaces.

AuthenticationAware is handled in a "stateful" manner - meaning that the current authentication token is handed to every new bean that is created. Whereas the LoginAware interface is handled in an "event" manner - meaning that beans that implement the interface are only updated when an event of the proper type occurs.

AuthenticationAware notification always takes place prior to LoginAware notifications. So, if you need to perform some operation that requires another bean to have its authentiation state updated, then you should do it in LoginAware (or watch for LoginEvent and LogoutEvent instances directly) as these are always delivered after the AuthenticationAware notifications. See below in the remoting section for a real example of why this matters.

Each security event is translated to a notification, as shown below.

EventNotification Made
AuthenticationEventAuthenticationAware.setAuthenticationToken
AuthenticationFailedEventno notifications made
LoginEventLoginAware.userLogin
LogoutEventLoginAware.userLogout

Note that for any of this to happen, the SecurityAwareConfigurer must be properly configured in the application context. Here is an example of that configuration:

 
    <bean id="securityAwareConfigurer"
          class="org.springframework.richclient.security.SecurityAwareConfigurer"
          lazy-init="false"/>
 
 

ClientSecurityEvent

The ApplicationSecurityManager is responsible for firing events that correspond to important security lifecycle events (authentication, login, logout, etc.). Specific subtypes represent each important event:

EventDescription
AuthenticationEventEvent fired when the user's authentication changes. This happens on both a successful login and a logout.
AuthenticationFailedEventEvent fired when an authentication attempt fails. This happens when a login is attempted and the authentication manager denies the authentication attempt.
LoginEventEvent fired when a new user logs in. This happens when a user successfully logs in, and after the AuthenticationEvent.
LogoutEventEvent fired when a user logs out. This happends when a user logs out, and after the AuthenticationEvent.

Any bean interested in these events should implement ApplicationListener and then watch for events that extend ClientSecurityEvent. If you want a "callback" mechanism instead of watching events, then a bean can implement AuthenticationAware and/or LoginAware.

How Login and Logout Works

This package provides two key RCP commands: LoginCommand and LogoutCommand. To use these commands, simply add them to your commands-context.xml, like this:

 
     <bean id="loginCommand"
          class="org.springframework.richclient.security.LoginCommand"/>

     <bean id="logoutCommand"
          class="org.springframework.richclient.security.LogoutCommand"/>
 
 

Both commands accept an optional property, displaySuccess, which defaults to true. This simply results in an information dialog being displayed after login or logout. You can switch this off by setting the property to false in the application context.

LoginCommand basically displays a dialog requesting the username and password. Upon these being entered, a request Authentication object is created (as mentioned in the Background Knowledge section above) and presented for authentication by calling ApplicationSecurityManager.doLogin. If the authentication succeeds, a populated Authentication object is stored as the "global" authentication toke (see above) and is returned to the caller (the LoginCommand). The returned token is also placed into the thread-specific ContextHolder (for a little bit of backward compatibility). As described above, the ApplicationSecurityManager publishes events so other interested classes know a login has taken place.

LogoutCommand is far simpler. It calls ApplicationSecurityManager.doLogout, which fires appropriate events, and then it updates the thread-specific ContextHolder so its Authentication object is null.

Which AuthenticationManager?

As mentioned above, an AuthenticationManager is configured against the LoginCommand. This is just like any other Acegi Security use of AuthenticationManager, so you can use any of the standard Acegi Security authentication providers with the RCP package (such as DaoAuthenticationProvider).

More typically, a rich client will need to use a remote server for authentication. In this case you need the client to ensure a username and password is valid against the remote server, and also obtain the list of GrantedAuthority[]s (so the populated Authentication object can be constructed). To achieve this, on the client you'll need to use the RemoteAuthenticationProvider. On the server you'll need to use the RemoteAuthenticationManagerImpl. On the client you'll use your preferred remoting proxy factory to access the server-side RemoteAuthenticationManagerImpl. You can find these classes in Acegi Security's org.acegisecurity.providers.rcp package. An example using the HTTP proxy would be configured like this:

 
        <bean id="applicationSecurityManager"
                class="org.springframework.richclient.security.support.DefaultApplicationSecurityManager">
                <property name="authenticationManager" ref="authenticationManager"/>
        </bean>

        <!-- Remote authentication manager configuration -->
        <bean id="authenticationManager"
                class="org.acegisecurity.providers.ProviderManager">
                <property name="providers">
                        <list>
                                <ref bean="remoteAuthenticationProvider" />
                        </list>
                </property>
        </bean>

        <bean id="remoteAuthenticationProvider"
                class="org.acegisecurity.providers.rcp.RemoteAuthenticationProvider">
                <property name="remoteAuthenticationManager" ref="remoteAuthenticationManager" />
        </bean>

        <bean id="remoteAuthenticationManager"
                class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
                <property name="serviceUrl">
                        <value>http://localhost:8080/myserver/context/RemoteAuthenticationManager</value>
                </property>
                <property name="serviceInterface">
                        <value>org.acegisecurity.providers.rcp.RemoteAuthenticationManager</value>
                </property>
        </bean>
 
 

Remoting Integration

HTTP proxies and Basic Authentication

Using HTTP invocation for remoting is one of the simplest mechanisms to configure. Two classes are provided to make using HTTP BASIC authentication on top of the simple HTTP remoting protocol. See the code sample above on how one might configure the use of the HTTP proxy factory.

org.springframework.richclient.security.remoting.BasicAuthHttpInvokerProxyFactoryBean and org.springframework.richclient.security.remoting.BasicAuthHttpInvokerRequestExecutor.

BasicAuthHttpInvokerProxyFactoryBean is an extension of HttpInvokerProxyFactoryBean that supports the use of BASIC authentication on each HTTP request. This factory takes care of instantiating the proper invocation executor, an BasicAuthHttpInvokerRequestExecutor, and keeping it up to date with the latest user credentials.

BasicAuthHttpInvokerProxyFactoryBean implements AuthenticationAware in order to get notifications of changes in the user's credentials. Please see the class documentation for AuthenticationAware above to see how to properly configure the application context so that authentication changes are broadcast properly.

Hession and Burlap proxies

If your application uses either the Hessian or Burlap remoting classes to access your business objects on the server, you will want to register RemotingSecurityConfigurer in your application context.

RemotingSecurityConfigurer listens for login and logout events and updates the usernames and passwords associated with any of your registered remoting proxy factories. This causes BASIC authentication to be used in the header of the remoting requests.

Server Side Configuration

On the server side you will need to register Acegi Security's BasicProcessingFilter so BASIC authentication headers can be processed. You'd need to do this if you're using Acegi Security with any form of BASIC authentication (it is not an RCP-specific requirement). Here is an example of how you might configure this in the application context of your server:

 
        <bean id="basicProcessingFilter"
                class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
                <property name="authenticationManager">
                        <ref bean="authenticationManager" />
                </property>
                <property name="authenticationEntryPoint">
                        <ref bean="basicProcessingFilterEntryPoint" />
                </property>
        </bean>

        <bean id="basicProcessingFilterEntryPoint"
                class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
                <property name="realmName">
                        <value>My Realm</value>
                </property>
        </bean>

        <bean id="httpSessionContextIntegrationFilter"
                class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
                <property name="allowSessionCreation">
                        <value>false</value>
                </property>
        </bean>

        <!-- Allows remote clients to check if a username/password is valid -->
        <bean id="remoteAuthenticationManager"
                class="org.acegisecurity.providers.rcp.RemoteAuthenticationManagerImpl">
                <property name="authenticationManager">
                        <ref bean="authenticationManager" />
                </property>
        </bean>

        <bean id="authenticationManager"
                class="org.acegisecurity.providers.ProviderManager">
                <property name="providers">
                        <list>
                                <ref bean="daoAuthenticationProvider" />
                        </list>
                </property>
        </bean>

        <bean id="daoAuthenticationProvider"
                class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
                <property name="authenticationDao">
                        <ref bean="authenticationDao" />
                </property>
        </bean>

        <!--  Special implementation to get authentication data  -->
        <bean id="authenticationDao"
                class="com.myco..security.MyAuthenticationDao">
        </bean>
 
 

And in the web.xml you might install it like this:

 
     <!-- Security configuration -->
     <filter>
         <filter-name>Acegi HTTP Session Integration</filter-name>
         <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
         <init-param>
             <param-name>targetClass</param-name>
             <param-value>org.acegisecurity.context.HttpSessionContextIntegrationFilter</param-value>
         </init-param>
     </filter>

     <filter-mapping>
       <filter-name>Acegi HTTP Session Integration</filter-name>
       <url-pattern>/context/*</url-pattern>
     </filter-mapping>

     <filter>
         <filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
         <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
         <init-param>
             <param-name>targetClass</param-name>
             <param-value>org.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
         </init-param>
     </filter>

     <filter-mapping>
       <filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
       <url-pattern>/context/*</url-pattern>
     </filter-mapping>
 
 

Action Control

Coming soon... The general idea will be CommandActions listen for events and update their visibility and enable/disabled status based on delegation to a security manager. The security manager will indicate the expected state based on granted authorities held.



Copyright © 2004-2008 The Spring Framework. All Rights Reserved.