You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Why use Acegi?

Acegi is a pretty complete solution for all kinds of security needs. It is very configurable and supports many authentication sources out of the box. These include: database queries, LDAP queries, CAS. It can be a bit complex to set up, but following the how to below should get you started quickly.

The example below shows how you can use Acegi in combination with Wicket-auth-roles. Acegi takes care of the authentication, Wicket-auth-roles does authorization. By this I mean that Acegi looks up the user (including roles, full name, etc.), validates the password, and keeps track of the logged on user in the current session. Wicket-auth-roles validates that the current user (can be none as well) has access to a particular page, or even a particular component.

The advantage of this setup is that you get to use a lot of Acegi functionality out of the box. Besides the mentioned authentication sources, you can for example also protect some services (spring beans) that are called by your Wicket application with an Acegi security proxy. Acegi's security proxies allow role based access (allowed if you have role x) but can also filter the results of a service call (e.g. user a is only allowed to see data where some amount is smaller then x).

How to user Wicket, Acegi and Wicket-auth-roles

It is assumed you have a Spring environment. How to set up a Spring environment is not explained here.

HOWTO TO BE FINISHED!!

Required on the classpath

  • Spring (it was assumed you already have this)
  • Acegi
  • Ehcache (optional, but assumed in this example)
  • Wicket-auth-roles

Acegi basic setup

First of all you need to set up Acegi. Somewhere in your Spring configs add:

<!-- Proxy to a set of filters that enforce authentication and authorization. -->
  <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
    <property name="filterInvocationDefinitionSource">
      <value>
        CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
        PATTERN_TYPE_APACHE_ANT
        /**=httpSessionContextIntegrationFilter
      </value>
    </property>
  </bean>

  <!-- Maintains security context between requests (using the session). -->
  <bean id="httpSessionContextIntegrationFilter"
    class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
    <property name="forceEagerSessionCreation" value="true"/>
  </bean>

   <!-- Users cache for Acegi (Ehcache). -->
   <bean id="userCache" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
      <property name="cache">
         <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
            <property name="cacheManager" ref="cacheManager"/>
            <property name="cacheName" value="your.application.name.USER_CACHE"/>
         </bean>
      </property>
   </bean>

   <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>

This also configures a user cache (very important when you use LDAP or database authentication). This particular configuration uses an EhCache cache.

Add the following to your web.xml:

    <filter>
        <filter-name>Acegi HTTP Request Security Filter</filter-name>
        <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>Acegi HTTP Request Security Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

You now have setup Acegi to put a security token in the 'security context' (which comes down to a thread local variable) during the invocation of each and every request, based on some information in the session. At the end of the request, the security token is removed from the security context.

You will also have to configure an authentication provider. Here are 2 examples, the first can be used during testing (all users configured), the second gets its data from LDAP.

Example 1: in-memory database of the users

Example 1 lists all users in the XML, providing a complete in-memory database of the users. The syntax of each user line is: user=password,full name,e-mail,role1,role2,....

    <!-- Acegi authentication manager that is configured with an in-memory authentication provider. -->
    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>

    <!-- Dao authentication provider with in-memory user details service. -->
    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="inMemoryDaoImpl"/>
        <property name="userCache" ref="userCache"/>
    </bean>

    <!-- In-memory user dao implementation. Use  -->
    <bean id="inMemoryDaoImpl" class="com.example.app.security.mock.InMemoryDaoImpl">
        <property name="userInfoMap">
            <value>
                fred=fredpw,Fred M. A. Nager,fred@example.com,ROLE_MANAGER,ROLE_SUBMITTER
                ella=ellapw,Ella E. Ditor,ella@example.com,ROLE_EDITOR,ROLE_SUBMITTER
            </value>
        </property>
    </bean>

Class com.example.app.security.mock.InMemoryDaoImpl is as follows:

/**
 * The class <code>InMemoryDaoImpl</code> is almost equivalent to its Acegi counterpart
 * {@link org.acegisecurity.userdetails.memory.InMemoryDaoImpl} but supports additional setting of
 * user displayname and e-mail address in the application context xml file.
 */
public class InMemoryDaoImpl implements UserDetailsService, InitializingBean {
    private UserInfoMap userInfoMap;

    /**
     * Checks whether the property <code>userInfoMap</code> is set.
     * @throws IllegalArgumentException if <code>userInfoMap</code> is <code>null</code>.
     */
    public void afterPropertiesSet() throws IllegalArgumentException {
        Assert.notNull(userInfoMap,
                "A list of users, passwords, e-mail addresses, displaynames and their granted authorities must be set");
    }

    /**
     * Implementation of the {@link org.acegisecurity.userdetails.UserDetailsService#loadUserByUsername} method
     * that retrieves the <code>UserDetails</code> instance from the map that is injected by Spring.
     * @param username the username of the user to retrieve
     * @return a <code>UserDetails</code> instance that corresponds to the given username
     * @throws UsernameNotFoundException if there is no user with the given username
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userInfoMap.getUser(username);
    }

    /**
     * Returns the user info map.
     * @return user info map
     */
    public UserInfoMap getUserInfoMap() {
        return userInfoMap;
    }

    /**
     * Sets the user info map (injected by Spring).
     * @param userInfoMap user info map
     * @see nl.amsterdam.rbrb.security.mock.UserInfoMapEditor
     */
    public void setUserInfoMap(UserInfoMap userInfoMap) {
        this.userInfoMap = userInfoMap;
    }
}

and the referenced UserInfoMap as follows:

public class UserInfoMap {

    private Map userMap = new HashMap();

    public UserDetails getUser(String username) throws org.acegisecurity.userdetails.UsernameNotFoundException {
        UserDetails user = (UserDetails) userMap.get(username);
        if (user == null) {
            throw new org.acegisecurity.userdetails.UsernameNotFoundException("Could not find user: " + username);
        }
        return user;
    }

    public void addUser(UserDetails user) {
        Assert.notNull(user, "User should not be null.");
        userMap.put(user.getUsername().toLowerCase(), user);
    }
}

UserDetails:

 

Example 2, LDAP configuration

    <!-- Authentication manager, configured with one provider that retrieves authentication information
        from an LDAP instance. -->
    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="ldapAuthenticationProvider"/>
            </list>
        </property>
    </bean>

    <!-- Example query against Active Directory, uses sAMAccountName as username -->
    <bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
        <constructor-arg index="0" value="ou=users,${__ldap.basedn}" />
        <constructor-arg index="1" value="(&amp;(objectclass=person)(sAMAccountName={0}))" />
        <constructor-arg index="2" ref="initialDirContextFactory" />
        <property name="searchSubtree" value="false" />
    </bean>

    <!-- Authentication provider for authentication via LDAP. -->
    <bean id="ldapAuthenticationProvider" class="com.example.app.security.AppLdapAuthenticationProvider">
        <constructor-arg>
            <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
                <constructor-arg ref="initialDirContextFactory"/>
                <property name="userSearch" ref="userSearch" />
            </bean>
        </constructor-arg>
        <constructor-arg>
            <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
                <constructor-arg ref="initialDirContextFactory"/>
                <constructor-arg>
                    <value>ou=groups,${__ldap.basedn}</value>
                </constructor-arg>
                <property name="groupSearchFilter" value="member={0}"/>
            </bean>
        </constructor-arg>
    </bean>

    <!-- Initial context factory. -->
    <bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
        <constructor-arg value="ldap://${__ldap.host}:${__ldap.port}/"/>
        <property name="managerDn" value="${__ldap.manager.cn}"/>
        <property name="managerPassword" value="${__ldap.manager.pass}"/>
    </bean>

    <!-- Property placeholder configurer for properties. -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="placeholderPrefix" value="${__"/>
        <property name="location" value="classpath:runtime.properties"/>
    </bean>

Again, class com.example.app.security.AppLdapAuthenticationProvider follows below.

With runtime.properties something like:

ldap.host=192.168.20.123
ldap.port=380
ldap.basedn=ou=Application,ou=Organisation,dc=example,dc=com
ldap.manager.cn=cn=manager,ou=users,ou=Application,ou=Organisation,dc=example,dc=com
ldap.manager.pass=secret

Here is the implementation of the in memory authentication provider:

  • No labels