0
votes

We are currently using apache shiro to authenticate users against a active directory.

Currently we have the users login via the ldap account name, like [email protected]

Now we should change the system, that the users can login via the sAMAccountName attribute. The idea is that the users enter the sAMAccountName name in the login box, and shiro then does match this against the [email protected] for the login.

The shiro.ini currently looks like this:

activeDirectoryRealm.systemUsername = systemuser
activeDirectoryRealm.systemPassword = *******
activeDirectoryRealm.searchBase = dc=corp,dc=adsdomain,dc=local
activeDirectoryRealm.url = ldap://<adsserver-ip>:389
activeDirectoryRealm.principalSuffix = @adsdomain.local
1

1 Answers

1
votes

With the help of this post in the shiro mailing list I was able to implement a working solution.

The basic steps are: 1. Implement your own queryForAuthenticationInfo method in a inherited class of the ActiveDirectoryRealm 2. Specify to use that new class for the query/login operation

public class AarstockADSRealm extends ActiveDirectoryRealm
{
    final private Logger _log = LoggerFactory.getLogger(AarstockADSRealm.class);

    public AarstockADSRealm()
    {
    }

    @Override
    protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException
    {
        //final AuthenticationInfo queryForAuthenticationInfo = super.queryForAuthenticationInfo(token, ldapContextFactory);
        final UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        LdapContext ctx = null;
        try
        {
            ctx = ldapContextFactory.getSystemLdapContext(); // .getLdapContext(upToken.getUsername(), upToken.getPassword());
            final String attribName = "userPrincipalName";
            final SearchControls searchCtls = new SearchControls(SearchControls.SUBTREE_SCOPE, 1, 0, new String[]
            {
                attribName
            }, false, false);
            final NamingEnumeration<SearchResult> search = ctx.search(searchBase, "sAMAccountName={0}", new Object[]
            {
                upToken.getPrincipal()
            }, searchCtls);
            if (search.hasMore())
            {
                final SearchResult next = search.next();
                // upToken.setUsername(next.getAttributes().get(attribName).get().toString());
                String loginUser= next.getAttributes().get(attribName).get().toString();
                _log.info("Loginuser: "+loginUser);
                if (search.hasMore())
                {
                    _log.error("More than one user matching: "+upToken.getPrincipal());
                    throw new RuntimeException("More than one user matching: "+upToken.getPrincipal());
                }
                else
                {
                    try
                    {
                        LdapContext ctx2 = ldapContextFactory.getLdapContext(loginUser, upToken.getPassword());
                    }
                    catch (Exception ex)
                    {
                        _log.warn("Error in authentication for user "+loginUser, ex);
                        // We have to rethrow the exception, to indicate invalid login
                        throw ex;
                    }
                }
            }
            else
            {
                _log.info("No user matching: "+upToken.getPrincipal());
                throw new RuntimeException("No user matching: "+upToken.getPrincipal());
            }
        }
        catch (NamingException ne)
        {
            _log.error("Error in ldap name resolving", ne);
                        // We have to rethrow the exception, to indicate invalid login
            throw ne;
        } finally
        {
            LdapUtils.closeContext(ctx);
        }
        return buildAuthenticationInfo(upToken.getUsername(), upToken.getPassword());
    }
}