Acegi Security System for Spring Reference Documentation 0.3 Ben Alex Preface This document provides a reference guide to the Acegi Security System for Spring, which is a series of classes that deliver authentication and authorization services within the Spring Framework. Whilst the Acegi Security System for Spring is not officially part of Spring, it is hoped this implementation will further discussion concerning the implementation of security capabilities within Spring itself. I would like to acknowledge this reference was prepared using the DocBook configuration included with the Spring Framework. The Spring team in turn acknowledge Chris Bauer (Hibernate) for his assistance with their DocBook. Security Introduction The Acegi Security System for Spring provides authentication and authorization capabilities for Spring-powered projects, with full integration with popular web containers. The security architecture was designed from the ground up using "The Spring Way" of development, which includes using bean contexts, interceptors and interface-driven programming. As a consequence, the Acegi Security System for Spring is useful out-of-the-box for those seeking to secure their Spring-based applications, and can be easily adapted to complex customized requirements. Security involves two distinct operations, authentication and authorization. The former relates to resolving whether or not a caller is who they claim to be. Authorization on the other hand relates to determining whether or not an authenticated caller is permitted to perform a given operation. Throughout the Acegi Security System for Spring, the user, system or agent that needs to be authenticated is referred to as a "principal". The security architecture does not have a notion of roles or groups, which you may be familiar with from other security implementations. Request Contexts Contexts Many applications require a way of sharing objects between classes, but without resorting to passing them in method signatures. This is commonly achieved by using a ThreadLocal. The Acegi Security System for Spring uses ThreadLocal functionality and introduces the concept of "request contexts". By placing an object into a request context, that object becomes available to any other object on the current thread of execution. The request context is not passed around as a method parameter, but is held in a ThreadLocal. The Acegi Security System for Spring uses the request context to pass around the authentication request and response. A request context is a concrete implementation of the Context interface, which exposes a single method: public void validate() throws ContextInvalidException; This validate() method is called to confirm the Context is properly setup. An implementation will typically use this method to check that the objects it holds are properly setup. The ContextHolder class makes the Context available to the current thread of execution using a ThreadLocal. A ContextInterceptor is also provided, which is intended to be chained into the bean context using ProxyFactoryBean. The ContextInterceptor simply calls Context.validate(), which guarantees to business methods that a valid Context is available from the ContextHolder. Secure Contexts The Acegi Security System for Spring requires the ContextHolder to contain a request context that implements the SecureContext interface. An implementation is provided named SecureContextImpl. The SecureContext simply extends the Context discussed above and adds a holder and validation for an Authentication object. Custom Contexts Developers can create their own request context classes to store application-specific objects. Such request context classes will need to implement the Context interface. If the Acegi Security System for Spring is to be used, developers must ensure any custom request contexts implement the SecureContext interface. Future Work Over time it is hoped that the Spring remoting classes can be extended to support propagation of the Context between ContextHolders on the client and server. Security Interception Configuration The security architecture is implemented by placing a properly configured SecurityInterceptor into the bean context, and then chaining that SecurityInterceptor into a business object. This chaining is accomplished using Spring’s ProxyFactoryBean, as commonly used by many other parts of Spring (refer to the security test cases and sample application for examples). The SecurityInterceptor is configured as follows: <bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor"> <property name="validateConfigAttributes"><value>true</value></property> <property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> <property name="runAsManager"><ref bean="runAsManager"/></property> <property name="methodDefinitionSource"> <value> net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER </value> </property> </bean> As shown above, the SecurityInterceptor is configured with a reference to an AuthenticationManager, AccessDecisionManager and RunAsManager, which are each discussed in separate sections below. The SecurityInterceptor is also configured with "configuration attributes" that apply to different method signatures. A configuration attribute is simply a ConfigAttribute instance that has special meaning to an AccessDecisionManager and/or RunAsManager. The SecurityInterceptor can be configured with configuration attributes in three ways. The first is via a property editor and the bean context, which is shown above. The second is via defining the configuration attributes in your source code using Commons Attributes. The third is via writing your own MethodDefinitionSource, although this is beyond the scope of this document. Irrespective of the approach used, the MethodDefinitionSource is responsible for returning a ConfigAttributeDefinition object that contains all of the configuration attributes associated with a single secure method. If using the property editor approach (as shown above), commas are used to delimit the different configuration attributes that apply to a given method pattern. Each configuration attribute is assigned into its own SecurityConfig object. SecurityConfig is a concrete implementation of ConfigAttribute, and simply stores the configuration attribute as a String. If using the Commons Attributes approach, your bean context will be configured differently: <bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/> <bean id="methodDefinitionSource" class="net.sf.acegisecurity.MethodDefinitionAttributes"> <property name="attributes"><ref local="attributes"/></property> </bean> <bean id="bankManagerSecurity" class="net.sf.acegisecurity.SecurityInterceptor"> <property name="validateConfigAttributes"><value>false</value></property> <property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> <property name="runAsManager"><ref bean="runAsManager"/></property> <property name="methodDefinitionSource"><ref bean="methodDefinitionSource"/></property> </bean> In addition, your source code will contain Commons Attributes tags that refer to a concrete implementation of ConfigAttribute. The following example uses the SecurityConfig implementation to represent the configuration attributes, and results in the same security configuration as provided by the property editor approach above: public interface BankManager { /** * @@SecurityConfig("ROLE_SUPERVISOR") * @@SecurityConfig("RUN_AS_SERVER") */ public void deleteSomething(int id); /** * @@SecurityConfig("ROLE_SUPERVISOR") * @@SecurityConfig("RUN_AS_SERVER") */ public void deleteAnother(int id); /** * @@SecurityConfig("ROLE_TELLER") * @@SecurityConfig("ROLE_SUPERVISOR") * @@SecurityConfig("BANKSECURITY_CUSTOMER") * @@SecurityConfig("RUN_AS_SERVER") */ public float getBalance(int id); } You might have noticed the validateConfigAttributes property in the above SecurityInterceptor examples. When set to true (the default), at startup time the SecurityInterceptor will evaluate if the provided configuration attributes are valid. It does this by checking each configuration attribute can be processed by either the AccessDecisionManager or the RunAsManager. If neither of these can process a given configuration attribute, an exception is thrown. If using the Commons Attributes method of configuration, you should set validateConfigAttributes to false. Runtime Processing At runtime the SecurityInterceptor has three basic tasks: authenticate, authorize and perform any run-as authentication replacement. It assesses the method pattern being invoked to determine whether or not it is secure. A secure method matches a method pattern defined with configuration attributes in the bean context, whilst a public method is not defined in the bean context. If a public method is called, SecurityInterceptor will make no effort to authenticate, authorize or perform any run-as authentication replacement for the request. However, should there be an Authentication object in the ContextHolder, it will have its authenticated property set to false. Once this is handled, invocation of the public method will proceed as normal. If a secure method is called, SecurityInterceptor will need to extract an Authentication object from the request context. As discussed above, the SecurityInterceptor requires the SecureContext interface be implemented on the object contained in the ContextHolder. Once the Authentication object is extracted from the SecureContext, it will be passed to the SecurityInterceptor’s AuthenticationManager. The AuthenticationManager will perform authentication, throwing an AuthenticationException if there is a problem. If successful, the AuthenticationManager will return a populated Authentication object, including the authorities granted to the principal. SecurityInterceptor will then call its AccessDecisionManager. When the AccessDecisionManager is invoked by the SecurityInterceptor, it will be passed important information it may require to make an authorization decision. This includes details of the secure method that is being invoked, the authenticated principal, and the ConfigAttributeDefinition (the collection of configuration attributes associated with the secure method). If authorization fails, the AccessDecisionManager will throw an AccessDeniedException. If successful, the SecurityInterceptor will then call the RunAsManager. Like the AccessDecisionManager, the RunAsManager is called with details of the secure method being invoked, the authenticated principal, and the ConfigAttributeDefinition. The RunAsManager can then choose to return a replacement Authentication object that should be used for the request. If a replacement Authentication object is returned, the SecurityInterceptor will update the ContextHolder for the duration of the method invocation, returning to the original Authentication object after the method has been invoked. Putting it into Context The above briefly outlines that the AuthenticationManager, AccessDecisionManager and RunAsManager perform the bulk of the security decision making. The SecurityInterceptor simply coordinates the method invocation and stores the configuration attributes that are relevant to different methods. It also coordinates the temporary replacement of the Authentication object as a consequence of RunAsManager responses. The way AuthenticationManager, AccessDecisionManager and RunAsManager operate is discussed in detail below. Authentication Authentication Requests Authentication requires a way for client code to present its security identification to the Acegi Security System for Spring. This is the role of the Authentication interface. The Authentication interface holds three important objects: the principal (the identity of the caller), the credentials (the proof of the identity of the caller, such as a password), and the authorities that have been granted to the principal. The principal and its credentials are populated by the client code, whilst the granted authorities are populated by the AuthenticationManager. The Acegi Security System for Spring includes several concrete Authentication implementations: UsernamePasswordAuthenticationToken allows a username and password to be presented as the principal and credentials respectively. TestingAuthenticationToken facilitates unit testing by automatically being considered an authenticated object by its associated AuthenticationProvider. RunAsUserToken is used by the default run-as authentication replacement implementation. This is discussed further in the Run-As Authentication Replacement section. PrincipalAcegiUserToken and JettyAcegiUserToken implement AuthByAdapter (a subclass of Authentication) and are used whenever authentication is completed by Acegi Security System for Spring container adapters. This is discussed further in the Container Adapters section. The authorities granted to a principal are represented by the GrantedAuthority interface. The GrantedAuthority interface is discussed at length in the Authorization section. Authentication Manager As discussed in the Security Interception section, the SecurityInterceptor extracts the Authentication object from the SecureContext. This is then passed to an AuthenticationManager. The AuthenticationManager interface is very simple: public Authentication authenticate(Authentication authentication) throws AuthenticationException; Implementations of AuthenticationManager are required to throw an AuthenticationException should authentication fail, or return a fully populated Authentication object. In particular, the returned Authentication object should contain an array of GrantedAuthority objects. The SecurityInterceptor places the populated Authentication object back in the SecureContext, overwriting the original Authentication object. The AuthenticationException has a number of subclasses. The most important are BadCredentialsException (an incorrect principal or credentials), DisabledException and LockedException. The latter two exceptions indicate the principal was found, but the credentials were not checked and authentication is denied. An AuthenticationServiceException is also provided, which indicates the authentication system could not process the request (eg a database was unavailable). Provider-Based Authentication Whilst the basic Authentication and AuthenticationManager interfaces enable users to develop their own authentication systems, users should consider using the provider-based authentication packages provided in the Acegi Security System for Spring. The key class, ProviderManager, is configured via the bean context with a list of AuthenticationProviders: <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"> <property name="providers"> <list> <ref bean="daoAuthenticationProvider"/> </list> </property> </bean> ProviderManager calls a series of registered AuthenticationProvider implementations, until one is found that indicates it is able to authenticate a given Authentication class. When the first compatible AuthenticationProvider is located, it is passed the authentication request. The AuthenticationProvider will then either throw an AuthenticationException or return a fully populated Authentication object. Note the ProviderManager may throw a ProviderNotFoundException (subclass of AuthenticationException) if it none of the registered AuthenticationProviders can validate the Authentication object. Several AuthenticationProvider implementations are provided with the Acegi Security System for Spring: TestingAuthenticationProvider is able to authenticate a TestingAuthenticationToken. The limit of its authentication is simply to treat whatever is contained in the TestingAuthenticationToken as valid. This makes it ideal for use during unit testing, as you can create an Authentication object with precisely the GrantedAuthority objects required for calling a given method. DaoAuthenticationProvider is able to authenticate a UsernamePasswordAuthenticationToken by accessing an authentication respository via a data access object. This is discussed further below. RunAsImplAuthenticationToken is able to authenticate a RunAsUserToken. This is discussed further in the Run-As Authentication Replacement section. AuthByAdapterProvider is able to authenticate any AuthByAdapter (a subclass of Authentication used with container adapters). This is discussed further in the Container Adapters section. Data Access Object Authentication Provider The Acegi Security System for Spring includes a production-quality AuthenticationProvider implementation called DaoAuthenticationProvider. This authentication provider is able to authenticate a UsernamePasswordAuthenticationToken by obtaining authentication details from a data access object configured at bean creation time: <bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"> <property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property> <property name="ignorePasswordCase"><value>false</value></property> <property name="ignoreUsernameCase"><value>true</value></property> </bean> By default the DaoAuthenticationProvider does not require an exact match on usernames, but it does require an exact match on passwords. This behavior can be configured with the optional properties shown above. For a class to be able to provide the DaoAuthenticationProvider with access to an authentication repository, it must implement the AuthenticationDao interface: public User loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException; The User object holds basic information such as the username, password, granted authorities and whether the user is enabled or disabled. Given AuthenticationDao is so simple to implement, it should be easy for users to retrieve authentication information using a persistence strategy of their choice. A design decision was made not to support account locking in the DaoAuthenticationProvider, as doing so would have increased the complexity of the AuthenticationDao interface. Such functionality could be easily provided in a new AuthenticationManager or AuthenticationProvider implementation. In-Memory Authentication Whilst it is easy to use the DaoAuthenticationProvider and create a custom AuthenticationDao implementation that extracts information from a persistence engine of choice, many applications do not require such complexity. One alternative is to configure an authentication repository in the bean context itself using the InMemoryDaoImpl: <bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl"> <property name="userMap"> <value> marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR dianne=emu,ROLE_TELLER scott=wombat,ROLE_TELLER peter=opal,disabled,ROLE_TELLER </value> </property> </bean> The userMap property contains each of the usernames, passwords, a list of granted authorities and an optional enabled/disabled keyword. Commas delimit each token. The username must appear to the left of the equals sign, and the password must be the first token to the right of the equals sign. The enabled and disabled keywords (case insensitive) may appear in the second or any subsequent token. Any remaining tokens are treated as granted authorities, which are created as GrantedAuthorityImpl objects (refer to the Authorization section for further discussion on granted authorities). Note that if a user has no password or no granted authorities, the user will not be created in the in-memory authentication repository. JDBC Authentication The Acegi Security System for Spring also includes an authentication provider that can obtain authentication information from a JDBC data source. The typical configuration for the JdbcDaoImpl is shown below: <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"><value>org.hsqldb.jdbcDriver</value></property> <property name="url"><value>jdbc:hsqldb:hsql://localhost:9001</value></property> <property name="username"><value>sa</value></property> <property name="password"><value></value></property> </bean> <!-- Data access object which stores authentication information --> <bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"> <property name="dataSource"><ref bean="dataSource"/></property> </bean> You can use different relational database management systems by modifying the DriverManagerDataSource shown above. Irrespective of the database used, a standard schema must be used as indicated in dbinit.txt. The Acegi Security System for Spring ships with a Hypersonic SQL instance that has the required authentication information and sample data already populated. To use this server, simply execute the server.bat or server.sh script included in the distribution. This will load a new database server instance that will service requests made to the URL indicated in the bean context configuration shown above. As discussed further in the Container Adapters section, most authentication providers that perform actual authentication are configured within a container adapter bean context typically called acegisecurity.xml. This file by default uses the in-memory DAO authentication provider. The Acegi Security System for Spring includes an alternative configuration file named acegisecurity-jdbc.xml, which uses the JDBC DAO authentication provider. To use a JDBC authentication repository, simply copy this file over the existing acegisecurity.xml (or other name) being used by your container. Alternatively, leave the name as acegisecurity-jdbc.xml and modify your container configuration file to refer to acegisecurity-jdbc.xml. You will also need to copy the relevant JDBC driver library to your container's lib directory. If you are using Hypersonic SQL, copy the hsqldb.jar file. Authentication Recommendations With the heavy use of interfaces throughout the authentication system (Authentication, AuthenticationManager, AuthenticationProvider and AuthenticationDao) it might be confusing to a new user to know which part of the authentication system to customize. In general, the following is recommended: Use the UsernamePasswordAuthenticationToken or an AuthByContainer implementation where possible. If you simply need to implement a new authentication repository (eg to obtain user details from your application’s existing database), use the DaoAuthenticationProvider along with the AuthenticationDao. It is the fastest and safest way to integrate an external database. Never enable the TestingAuthenticationProvider on a production system. Doing so will allow any client to simply present a TestingAuthenticationToken and obtain whatever access they request. Adding a new AuthenticationProvider is sufficient to support most custom authentication requirements. Only unusual requirements would require the ProviderManager to be replaced with a different AuthenticationManager. Authorization Granted Authorities As briefly mentioned in the Authentication section, all Authentication implementations are required to store an array of GrantedAuthority objects. These represent the authorities that have been granted to the principal. The GrantedAuthority objects are inserted into the Authentication object by the AuthenticationManager and are later read by AccessDecisionManagers when making authorization decisions. GrantedAuthority is an interface with only one method: public String getAuthority(); This method allows AccessDecisionManagers to obtain a precise String representation of the GrantedAuthority. By returning a representation as a String, a GrantedAuthority can be easily "read" by most AccessDecisionManagers. If a GrantedAuthority cannot be precisely represented as a String, the GrantedAuthority is considered "complex" and getAuthority() must return null. An example of a "complex" GrantedAuthority would be an implementation that stores a list of operations and authority thresholds that apply to different customer account numbers. Representing this complex GrantedAuthority as a String would be quite complex, and as a result the getAuthority() method should return null. This will indicate to any AccessDecisionManager that it will need to specifically support the GrantedAuthority implementation in order to understand its contents. The Acegi Security System for Spring includes one concrete GrantedAuthority implementation, GrantedAuthorityImpl. This allows any user-specified String to be converted into a GrantedAuthority. All AuthenticationProviders included with the security architecture used GrantedAuthorityImpl to populate the Authentication object. Access Decision Managers The AccessDecisionManager is called by the SecurityInterceptor and is responsible for making final access control decisions. The AccessDecisionManager interface contains two methods: public void decide(Authentication authentication, MethodInvocation invocation, ConfigAttributeDefinition config) throws AccessDeniedException; public boolean supports(ConfigAttribute attribute); As can be seen from the first method, the AccessDecisionManager is passed via method parameters all information that is likely to be of value in assessing an authorization decision. In particular, passing the MethodInvocation enables those arguments contained in the intercepted method call to be inspected. For example, if a Customer argument was passed in the intercepted method call, this could be extracted from the MethodInvocation and used in making an access control decision. Implementations are expected to throw an AccessDeniedException if access is denied. The supports(ConfigAttribute) method is called by the SecurtyInterceptor at startup time to determine if the AccessDecisionManager can process the passed ConfigAttribute. Voting Decision Manager Whilst users can implement their own AccessDecisionManager to control all aspects of authorization, the Acegi Security System for Spring includes an AccessDecisionManager implementation that is based on voting. Using this approach, a series of AccessDecisionVoter implementations are polled on an authorization decision. The AccessDecisionManager then decides whether or not to throw an AccessDeniedException based on its assessment of the votes. The AccessDecisionVoter interface has two methods: public int vote(Authentication authentication, MethodInvocation invocation, ConfigAttributeDefinition config); public boolean supports(ConfigAttribute attribute); Concrete implementations return an int, with possible values being reflected in the AccessDecisionVoter static fields ACCESS_ABSTAIN, ACCESS_DENIED and ACCESS_GRANTED. A voting implementation will return ACCESS_ABSTAIN if it has no opinion on an authorization decision. If it does have an opinion, it must return either ACCESS_DENIED or ACCESS_GRANTED. There are three AccessDecisionManagers provided with the Acegi Security System for Spring that tally the votes. The ConsensusBased implementation will grant or deny access based on the consensus of non-abstain votes. Properties are provided to control behavior in the event of an equality of votes or if all votes are abstain. The AffirmativeBased implementation will grant access if one or more ACCESS_GRANTED votes were received (ie a deny vote will be ignored, provided there was at least one grant vote). Like the ConsensusBased implementation, there is a parameter that controls the behavior if all voters abstain. The UnanimousBased provider will deny access if there was any ACCESS_DENIED result. Like the other implementations, there is a parameter that controls the behaviour if all voters abstain. It is possible to implement a custom AccessDecisionManager that tallies votes differently. For example, votes from a particular AccessDecisionVoter might receive additional weighting, whilst a deny vote from a particular voter would have a veto effect. There is one concrete AccessDecisionVoter implementation provided with the Acegi Security System for Spring. The RoleVoter class will vote if any ConfigAttribute begins with ROLE_. It will vote to grant access if there is a GrantedAuthority which returns a String representation (via the getAuthority() method) exactly equal to one or more ConfigAttributes starting with ROLE_. If there is no exact match of any ConfigAttribute starting with ROLE_, the RoleVoter will vote to deny access. If no ConfigAttribute begins with ROLE_, the voter will abstain. RoleVoter is case sensitive on comparisons as well as the ROLE_ prefix. It is possible to implement a custom AccessDecisionVoter. Several examples are provided in the Acegi Security System for Spring unit tests, including BankSecurityVoter and XVoter. The BankSecurityVoter abstains from voting decisions where the BANKSECURITY_CUSTOMER ConfigAttribute is not found. If voting, it queries the MethodInvocation to extract the account number subject of the method call. It votes to grant access if the account number matches a GrantedAuthority.getAuthority() of ACCOUNT_xxxx where xxxx is the account number subject of the method call. All of this is achieved with relatively few lines of code and demonstrates the flexibility of the authorization model. Note that an AccessDecisionManager or AccessDecisionVoter can also support complex GrantedAuthority implementations that cannot represent themselves as a String via the getAuthority() method. In our BankSecurityVoter example, we could have it ignore the String representations of GrantedAuthority objects but instead processed any AccountHolderGrantedAuthority objects. This complex granted authority could have conveyed more information than simply an account number, such as the maximum amount the principal is permitted to deposit. The BankSecurityVoter could then detect a deposit value via the MethodInvocation and grant or deny access accordingly. Authorization Recommendations Given there are several ways to achieve similar authorization outcomes in the Acegi Security System for Spring, the following general recommendations are made: Grant authorities using GrantedAuthorityImpl where possible. Because it is already supported by the Acegi Security System for Spring, you avoid the need to create custom AuthenticationManager or AuthenticationProvider implementations simply to populate the Authentication object with a custom GrantedAuthority. Most authorization decision rules can be easily satisfied by writing an AccessDecisionVoter implementation and using either ConsensusBased or AffirmativeBased as the AccessDecisionManager. Authorization Tag Library The Acegi Security System for Spring comes bundled with a JSP tag library that eases JSP writing. Installation Usage The following JSP fragment illustrates how to use the authz taglib: <authz:authorize ifAllGranted="ROLE_SUPERVISOR"> <td> <A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A> </td> </authz:authorize> What this code says is: if the pricipal has been granted ROLE_SUPERVISOR, allow the tag's body to be output. Run-As Authentication Replacement Purpose The SecurityInterceptor is able to temporarily replace the Authentication object in the ContextHolder during a method invocation. This only occurs if the original Authentication object was successfully processed by the AuthenticationManager and AccessDecisionManager. The RunAsManager will indicate the replacement Authentication object (if any) that should be used during the method invocation. By temporarily replacing the Authentication object during a method invocation, the method invocation will be able to call other objects which require different authentication and authorization credentials. It will also be able to perform any internal security checks for specific GrantedAuthority objects. Usage A RunAsManager interface is provided by the Acegi Security System for Spring: public Authentication buildRunAs(Authentication authentication, MethodInvocation invocation, ConfigAttributeDefinition config); public boolean supports(ConfigAttribute attribute); The first method returns the Authentication object that should replace the existing Authentication object for the duration of the method invocation. If the method returns null, it indicates no replacement should be made. The second method is used by the SecurityInterceptor as part of its startup validation of configuration attributes. One concrete implementation of a RunAsManager is provided with the Acegi Security System for Spring. The RunAsManagerImpl class returns a replacement RunAsUserToken if any ConfigAttribute starts with RUN_AS_. If any such ConfigAttribute is found, the replacement RunAsUserToken will contain the same principal, credentials and granted authorities as the original Authentication object, along with a new GrantedAuthorityImpl for each RUN_AS_ ConfigAttribute. Each new GrantedAuthorityImpl will be prefixed with ROLE_, followed by the RUN_AS ConfigAttribute. For example, a RUN_AS_SERVER will result in the replacement RunAsUserToken containing a ROLE_RUN_AS_SERVER granted authority. The replacement RunAsUserToken is just like any other Authentication object. It needs to be authenticated by the AuthenticationManager, probably via delegation to a suitable AuthenticationProvider. The RunAsImplAuthenticationProvider performs such authentication. It simply accepts as valid whatever RunAsUserToken is presented. To ensure malicious code does not create a RunAsUserToken and presents it for guaranteed acceptance by the RunAsImplAuthenticationProvider, the hash of a key is stored in all generated tokens. The RunAsManagerImpl and RunAsImplAuthenticationProvider is created in the bean context with the same key: <bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl"> <property name="key"><value>my_run_as_password</value></property> </bean><bean id="runAsAuthenticationProvider" class="net.sf.acegisecurity.runas.RunAsImplAuthenticationProvider"> <property name="key"><value>my_run_as_password</value></property> </bean> By using the same key, each RunAsUserToken can be validated it was created by an approved RunAsManagerImpl. The RunAsUserToken is immutable after creation for security reasons. Container Adapters Overview A very large proportion of Spring applications are web-based. The Acegi Security System for Spring supports integration with containers that host such applications. This integration means that applications can continue to leverage the authentication and authorization capabilities built into containers (such as isUserInRole() and form-based or basic authentication), whilst benefiting from the enhanced bean-level security capabilities provided by the Acegi Security System for Spring. The integration between a container and the Acegi Security System for Spring is achieved through an adapter. The adapter provides a container-compatible user authentication provider, and often needs to return a container-compatible user object. The adapter is instantiated by the container and is defined in a container-specific configuration file. The adapter then loads a Spring bean context which defines the normal authentication manager settings, such as the authentication providers that can be used to authenticate the request. The bean context is usually named acegisecurity.xml and is placed in a container-specific location. The Acegi Security System for Spring currently supports Jetty, Catalina (Tomcat), JBoss and Resin. Additional container adapters can easily be written. Filter Integration Web applications wishing to use container adapters should define the standard <security-constraint> and <login-config> entries in their web.xml file. These will cause the container authentication to occur, which will delegate to the Acegi Security System for Spring provided adapter. The adapter will return a container-compatible user object that also implements the Authentication interface. The container then makes this returned user object available from a container-specific well-known location. The AbstractIntegrationFilter and its subclasses finalise the adapter integration. These classes are standard filters, and at the start of each request they will attempt to extract the Authentication object from the container's well-known location. The Authentication object will then be associated with the ContextHolder for the duration of the request, and be removed when the request is finished. Three concrete subclasses of AbstractIntegrationFilter are provided with the Acegi Security System for Spring: HttpRequestIntegrationFilter is used with Catalina, Jetty and Resin. It extracts the authentication information from HttpServletRequest.getUserPrincipal(). JbossIntegrationFilter is used with JBoss. It extracts the authentication from java:comp/env/security/subject. AutoIntegrationFilter automatically determines which filter to use. This makes a web application WAR file more portable, as the web.xml is not hard-coded to a container-specific AbstractIntegrationFilter. Once in the ContextHolder, the standard Acegi Security System for Spring classes can be used. Because ContextHolder is a standard object which is populated using a filter at the container level, JSPs and Servlets do not need to use Spring's MVC packages. This enables those applications that use other MVC frameworks to still leverage Spring's other capabilities, with full authentication and authorization support. The debug.jsp page provided with the sample application demonstrates accessing the ContextHolder independent of Spring's MVC packages. Adapter Authentication Provider As is always the case, the container adapter generated Authentication object still needs to be authenticated by an AuthenticationManager when requested to do so by the SecurityInterceptor. The AuthenticationManager needs to be certain the adapter-provided Authentication object is valid and was actually authenticated by a trusted adapter. Adapters create Authentication objects which are immutable and implement the AuthByAdapter interface. These objects store the hash of a key that is defined by the adapter. This allows the Authentication object to be validated by the AuthByAdapterProvider. This authentication provider is defined as follows: <bean id="authByAdapterProvider" class="net.sf.acegisecurity.adapters.AuthByAdapterProvider"> <property name="key"><value>my_password</value></property> </bean> The key must match the key that is defined in the container-specific configuration file that starts the adapter. The AuthByAdapterProvider automatically accepts as valid any AuthByAdapter implementation that returns the expected hash of the key. To reiterate, this means the adapter will perform the initial authentication using providers such as DaoAuthenticationProvider, returning an AuthByAdapter instance that contains a hash code of the key. Later, when an application calls a SecurityInterceptor managed bean, the AuthByAdapter instance in the ContextHolder will be tested by the application's AuthByAdapterProvider. There is no requirement for additional authentication providers such as DaoAuthenticationProvider within the application-specific bean context, as the only type of Authentication instance that will be presented by the application is from the container adapter. Classloader issues are frequent with containers and the use of container adapters illustrates this further. Each container requires a very specific configuration. The installation instructions are provided below. Once installed, please take the time to try the sample application to ensure your container adapter is properly configured. Catalina (Tomcat) Installation The following was tested with Jakarta Tomcat 5.0.19. We automatically test the following directions using our container integration test system and this version of Catalina (Tomcat). $CATALINA_HOME refers to the root of your Catalina (Tomcat) installation. Edit your $CATALINA_HOME/conf/server.xml file so the <Engine> section contains only one active <Realm> entry. An example realm entry: <Realm className="net.sf.acegisecurity.adapters.catalina.CatalinaAcegiUserRealm" appContextLocation="conf/acegisecurity.xml" key="my_password" /> Be sure to remove any other <Realm> entry from your <Engine> section. Copy acegisecurity.xml into $CATALINA_HOME/conf. Copy acegi-security-catalina-server.jar into $CATALINA_HOME/server/lib. Copy the following files into $CATALINA_HOME/common/lib: aopalliance.jar spring.jar acegi-security-catalina-common.jar None of the above JAR files (or acegi-security.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with Catalina. Jetty Installation The following was tested with Jetty 4.2.18. We automatically test the following directions using our container integration test system and this version of Jetty. $JETTY_HOME refers to the root of your Jetty installation. Edit your $JETTY_HOME/etc/jetty.xml file so the <Configure class> section has a new addRealm call: <Call name="addRealm"> <Arg> <New class="net.sf.acegisecurity.adapters.jetty.JettyAcegiUserRealm"> <Arg>Spring Powered Realm</Arg> <Arg>my_password</Arg> <Arg>/etc/acegisecurity.xml</Arg> </New> </Arg> </Call> Copy acegisecurity.xml into $JETTY_HOME/etc. Copy the following files into $JETTY_HOME/ext: aopalliance.jar commons-logging.jar spring.jar acegi-security-jetty-ext.jar None of the above JAR files (or acegi-security.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does matter with Jetty. The web.xml must express the same <realm-name> as your jetty.xml (in the example above, "Spring Powered Realm"). JBoss Installation The following was tested with JBoss 3.2.3. We automatically test the following directions using our container integration test system and this version of JBoss. $JBOSS_HOME refers to the root of your JBoss installation. Edit your $JBOSS_HOME/server/your_config/conf/login-config.xml file so that it contains a new entry under the <Policy> section: <application-policy name = "SpringPoweredRealm"> <authentication> <login-module code = "net.sf.acegisecurity.adapters.jboss.JbossSpringLoginModule" flag = "required"> <module-option name = "appContextLocation">acegisecurity.xml</module-option> <module-option name = "key">my_password</module-option> </login-module> </authentication> </application-policy> Copy acegisecurity.xml into $JBOSS_HOME/server/your_config/conf. Copy the following files into $JBOSS_HOME/server/your_config/lib: aopalliance.jar spring.jar acegi-security-jboss-lib.jar None of the above JAR files (or acegi-security.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with JBoss. However, your web application's WEB-INF/jboss-web.xml must express the same <security-domain> as your login-config.xml. For example, to match the above example, your jboss-web.xml would look like this: <jboss-web> <security-domain>java:/jaas/SpringPoweredRealm</security-domain> </jboss-web> Resin Installation The following was tested with Resin 3.0.6. $RESIN_HOME refers to the root of your Resin installation. Resin provides several ways to support the container adapter. In the instructions below we have elected to maximise consistency with other container adapter configurations. This will allow Resin users to simply deploy the sample application and confirm correct configuration. Developers comfortable with Resin are naturally able to use its capabilities to package the JARs with the web application itself, and/or support single sign-on. Copy the following files into $RESIN_HOME/lib: aopalliance.jar commons-logging.jar spring.jar acegi-security-resin-lib.jar Unlike the container-wide acegisecurity.xml files used by other container adapters, each Resin web application will contain its own WEB-INF/resin-acegisecurity.xml file. Each web application will also contain a resin-web.xml file which Resin uses to start the container adapter: <web-app> <authenticator> <type>net.sf.acegisecurity.adapters.resin.ResinAcegiAuthenticator</type> <init> <app-context-location>WEB-INF/resin-acegisecurity.xml</app-context-location> <key>my_password</key> </init> </authenticator> </web-app> With the basic configuration provided above, none of the JAR files listed (or acegi-security.jar) should be in your application's WEB-INF/lib. The realm name indicated in your web.xml does not matter with Resin, as the relevant authentication class is indicated by the <authenticator> setting. Sample Application Included with the Acegi Security System for Spring is a very simple application that can demonstrate the basic security facilities provided by the system and confirm your container adapter is properly configured. To install, configure your container as described in the Container Adapters section of this chapter. Do not modify acegisecurity.xml. It contains a very basic in-memory authentication configuration that is compatible with the sample application. Next, copy the contacts.war file from the Acegi Security System for Spring distribution into your container’s webapps directory. After starting your container, check the application can load. Visit http://localhost:8080/contacts (or whichever URL is appropriate for your web container). A random contact should be displayed. Click "Refresh" several times and you will see different contacts. The business method that provides this random contact is not secured. Next, click "Debug". You will be prompted to authenticate, and a series of usernames and passwords are suggested on that page. Simply authenticate with any of these and view the resulting page. It should contain a success message similar to the following:
Context on ContextHolder is of type: net.sf.acegisecurity.context.SecureContextImpl The Context implements SecureContext. Authentication object is of type: net.sf.acegisecurity.adapters.PrincipalAcegiUserToken Authentication object as a String: net.sf.acegisecurity.adapters.PrincipalAcegiUserToken@e9a7c2: Username: marissa; Password: [PROTECTED]; Authenticated: true; Granted Authorities: ROLE_TELLER, ROLE_SUPERVISOR Authentication object holds the following granted authorities: ROLE_TELLER (getAuthority(): ROLE_TELLER) ROLE_SUPERVISOR (getAuthority(): ROLE_SUPERVISOR) SUCCESS! Your container adapter appears to be properly configured!
If you receive a different message, check you have properly configured your container adapter. Refer to the instructions provided above. Once you successfully receive the above message, return to the sample application's home page and click "Manage". You can then try out the application. Notice that only the contacts belonging to the currently logged on user are displayed, and only users with ROLE_SUPERVISOR are granted access to delete their contacts. Behind the scenes, the SecurityInterceptor is securing the business objects.
Become Involved We welcome you to become involved in the Acegi Security System for Spring project. There are many ways of contributing, including reading the mailing list and responding to questions from other people, writing new code, improving existing code, assisting with documentation, or simply making suggestions. SourceForge provides CVS services for the project, allowing anybody to access the latest code. If you wish to contribute new code, please observe the following requirements. These exist to maintain the quality and consistency of the project: Run the Ant format task to convert your code into the project's consistent style Ensure your code does not break any unit tests (run the Ant tests target) Please use the container integration test system to test your code in the project's officially supported containers When writing a new container adapter, expand the container integration test system to properly test it If you have added new code, please provide suitable unit tests Add a CVS $Id$ tag to the JavaDocs for any new class you create Mentioned above is our container integration test system, which aims to test the Acegi Security System for Spring container adapters with current, production versions of each container. Some containers might not be supported due to difficulties with starting or stopping the container within an Ant target. You will need to download the container release files as specified in the integration test readme.txt file. These files are intentionally excluded from CVS due to their large size. Further Information Questions and comments on the Acegi Security System for Spring are welcome. Please direct comments to the Spring Users mailing list or ben.alex@acegi.com.au. Our project home page (where you can obtain the latest release of the project and access to CVS) is at http://acegisecurity.sourceforge.net.