Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: link to acegi and swarm howto added

...

A new Wicket project is currently in the works. You can read more about it on http://wicketstuff.org/confluence/display/STUFFWIKI/Wicket-Security. Please investigate whether it will suite your needs better.

For those still wanting to use Acegi, there is an howto on getting Swarm working with Acegi http://wicketstuff.org/confluence/display/STUFFWIKI/Swarm+and+Acegi+HowTo 

Complete example

This example is extracted from a production system running on Wicket 1.2.6, Spring 2.0.5 and Acegi 1.0.2 on a 1.4 JVM. Wicket-auth-roles requires Java 5, but is quite simple to port it to Java 1.4 by removing everything related to annotations. If you do not want to port Wicket-auth-roles, this example requires that you use Java 5.

...

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

No Format
  <!-- 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"/>

...

Add the following to your web.xml:

No Format
    <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>

...

No Format
public class YourAppUserDetailsLdapImpl implements YourAppUserDetails, org.acegisecurity.userdetails.ldap.LdapUserDetails {

    private static final String LDAP_ATTRIBUTENAME_MAIL = "mail";
    private static final String LDAP_ATTRIBUTENAME_DISPLAYNAME = "displayname";

    /** The wrapped <code>LdapUserDetails</code> instance. */
    private final LdapUserDetails ldapUserDetails;

    private String email;
    private String displayname;

    /**
     * Wrap the given Acegi <code>LdapUserDetails</code> instance. This class adds the additional properties
     * <code>email</code> and <code>displayname</code> that are fetched from the <code>LdapUserDetails</code>
     * {@link org.acegisecurity.userdetails.ldap.LdapUserDetails#getAttributes() attributes}.
     *
     * Note: an e-mail address is mandatory, full name is not.
     *
     * @param ldapUserDetails the wrapped user details instance
     */
    public YourAppUserDetailsLdapImpl(final LdapUserDetails ldapUserDetails) {
        this.ldapUserDetails = ldapUserDetails;
        Attributes attributes = this.ldapUserDetails.getAttributes();

        // Fetch e-mail address from attributes (required, exception is thrown if not available).
        try {
            Attribute mailAttribute = attributes.get(LDAP_ATTRIBUTENAME_MAIL);
            email = (String) (mailAttribute == null ? null : mailAttribute.get());
            if (email == null) {
                String errorMessage = "No attribute named '" + LDAP_ATTRIBUTENAME_MAIL
                        + "' found for user '" + ldapUserDetails.getUsername() + "'.";
                throw new RuntimeException(errorMessage);
            }

        } catch (NamingException e) {
            String errorMessage = "NamingException while attempting to retrieve value for attribute '"
                    + LDAP_ATTRIBUTENAME_MAIL + "' for user '" + ldapUserDetails.getUsername() + "'.";
            throw new RuntimeException(errorMessage, e);
        }

        // Get Display name
        try {
            Attribute displaynameAttribute = attributes.get(LDAP_ATTRIBUTENAME_DISPLAYNAME);
            displayname = (String) (displaynameAttribute == null ? null : displaynameAttribute.get());

        } catch (NamingException e) {
            LOG.warn("NamingException while attempting to retrieve value for attribute '"
                    + LDAP_ATTRIBUTENAME_DISPLAYNAME
                    + "' for user '" + ldapUserDetails.getUsername() + "'. Setting displayname to null.");
        }
    }

    /** {@inheritDoc} */
    public String getEmail() {
        return email;
    }

    /** {@inheritDoc} */
    public String getDisplayname() {
        return displayname;
    }

    /**
     * @return this user's authorities (i.e. roles)
     */
    public GrantedAuthority[] getAuthorities() {
        return ldapUserDetails.getAuthorities();
    }

    /**
     * @return this user's password
     */
    public String getPassword() {
        return ldapUserDetails.getPassword();
    }

    /**
     * @return this user's user name
     */
    public String getUsername() {
        return ldapUserDetails.getUsername();
    }

    /** {@inheritDoc} */
    public boolean isAccountNonExpired() {
        return ldapUserDetails.isAccountNonExpired();
    }

    /** {@inheritDoc} */
    public boolean isAccountNonLocked() {
        return ldapUserDetails.isAccountNonLocked();
    }

    /** {@inheritDoc} */
    public boolean isCredentialsNonExpired() {
        return ldapUserDetails.isCredentialsNonExpired();
    }

    /** {@inheritDoc} */
    public boolean isEnabled() {
        return ldapUserDetails.isEnabled();
    }

    /**
     * @return this user's LDAP attributes
     */
    public Attributes getAttributes() {
        return ldapUserDetails.getAttributes();
    }

    /**
     * Returns any LDAP response controls that were part of the user authentication process. See
     * <a href="ftp://ftp.isi.edu/in-notes/rfc2251.txt" >RFC 2251</a> for a description of controls.
     * @return LDAP response controls
     */
    public Control[] getControls() {
        return ldapUserDetails.getControls();
    }

    /**
     * @return this user's distinguished name
     */
    public String getDn() {
        return ldapUserDetails.getDn();
    }
}

...

Finally the configuration of the LDAP authenticatoin provider. Again this is done in a Spring config file:

No Format
    <!-- 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.YourAppLdapAuthenticationProvider">
        <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 for JNDI queries to LDAP server. -->
    <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>

    <!-- Read LDAP properties from a file. -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="placeholderPrefix" value="${__"/>
        <property name="location" value="classpath:ldap.properties"/>
    </bean>

...

No Format
import org.acegisecurity.*;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;

public class YourAppSession extends AuthenticatedWebSession {
    public YourAppSession(final AuthenticatedWebApplication application) {
        super(application);
    }

    /**
     * Attempts to authenticate a user that has provided the given username and password.
     * @param username current username
     * @param password current password
     * @return <code>true</code> if authentication succeeds, <code>false</code> otherwise
     */
    public boolean authenticate(String username, String password) {
        String u = username == null ? "" : username;
        String p = password == null ? "" : password;

        // Create an Acegi authentication request.
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(u, p);

        // Attempt authentication.
        try {
            AuthenticationManager authenticationManager =
                ((YourAppApplication) getApplication()).getAuthenticationManager();
            Authentication authResult = authenticationManager.authenticate(authRequest);
            setAuthentication(authResult);

            LOG.info("Login by user '" + username + "'.");
            return true;

        } catch (BadCredentialsException e) {
            LOG.info("Failed login by user '" + username + "'.");
            setAuthentication(null);
            return false;

        } catch (AuthenticationException e) {
            LOG.error("Could not authenticate a user", e);
            setAuthentication(null);
            throw e;

        } catch (RuntimeException e) {
            LOG.error("Unexpected exception while authenticating a user", e);
            setAuthentication(null);
            throw e;
        }
    }

    /**
     * @return the currently logged in user, or null when no user is logged in
     */
    public YourAppUserDetails getUser() {
        YourAppUserDetails user = null;
        if (isSignedIn()) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            user = (YourAppUserDetails) authentication.getPrincipal();
        }
        return user;
    }

    /**
     * Returns the current user roles.
     * @return current user roles
     */
    public Roles getRoles() {
        if (isSignedIn()) {
            Roles roles = new Roles();
            // Retrieve the granted authorities from the current authentication. These correspond one on
            // one with user roles.
            GrantedAuthority[] authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
            for (int i = 0; i < authorities.length; i++) {
                GrantedAuthority authority = authorities[i];
                roles.add(authority.getAuthority());
            }
            return roles;
        }
        return null;
    }

    /**
     * Signout, invalidates the session. After a signout, you should redirect the browser to the home page.
     */
    public void signout() {
        YourAppUserDetails user = getUser();
        if (user != null) {
            LOG.info("Logout by user '" + user.getUsername() + "'.");
        }
        setAuthentication(null);
        invalidate();
    }

    /**
     * Sets the acegi authentication.
     * @param authentication the authentication or null to clear 
     */
    private void setAuthentication(Authentication authentication) {
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    /**
     * @return the current YourApp session
     */
    public static YourAppSession getYourAppSession() {
        return (YourAppSession) Session.get();
    }

}

...