I want to cache LDAP user data locally to allow faster queries. Do the Spring LDAP offers such a functionality? How can I do this?
I am using Spring Security 3.1 and Spring LDAP 1.3.1 for authentication and authorization. It would be nice to have a cache for LDAP using built-in mechanism if exists..
applicationContext-ldap.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
">
<!-- Ldap -->
<jee:jndi-lookup id="ldapUrl" jndi-name="appName/ldapUrl" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapUser" jndi-name="appName/ldapUser" expected-type="java.lang.String" />
<jee:jndi-lookup id="ldapPassword" jndi-name="appName/ldapPassword" expected-type="java.lang.String" />
<!-- for authentication and search purpose -->
<bean id="ldapContextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" ref="ldapUrl" />
<property name="userDn" ref="ldapUser" />
<property name="password" ref="ldapPassword" />
<property name="pooled" value="true" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<property name="contextSource" ref="ldapContextSource" />
</bean>
<!-- for pagination search purpose -->
<bean id="dirContext" factory-bean="ldapContextSource" factory-method="getReadOnlyContext" scope="session"/>
<bean id="singleLdapContextSource" class="org.springframework.ldap.core.support.SingleContextSource" scope="session">
<constructor-arg ref="dirContext"/>
</bean>
<bean id="singleLdapTemplate" class="org.springframework.ldap.core.LdapTemplate" scope="session">
<property name="contextSource" ref="singleLdapContextSource" />
</bean>
</beans>
spring-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<!-- This is where we configure Spring-Security -->
<security:http
auto-config="true"
use-expressions="true"
access-denied-page="/auth/denied"
>
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/app/admin" access="permitAll"/>
<security:intercept-url pattern="/app/common" access="hasRole('User')"/>
<security:intercept-url pattern="/viol/home" access="permitAll"/>
<security:intercept-url pattern="/app/users" access="permitAll"/>
<security:intercept-url pattern="/admin/edit/*" access="hasRole('Administrator')"/>
<security:form-login
login-page="/auth/login"
authentication-failure-url="/auth/loginFailure"
default-target-url="/auth/authorize"/>
<security:logout
invalidate-session="true"
logout-success-url="/auth/login"
logout-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:ldap-authentication-provider
server-ref="ldapContextSource"
user-search-filter="(sAMAccountName={0})"
user-search-base="dc=myDomain,dc=com"
/>
</security:authentication-manager>
</beans>
Thank you very much for your help!
If you configure EhCacheBasedUserCache and use ldap-user-service then you can use cache as:
<authentication-manager>
<authentication-provider>
<ldap-user-service
user-search-filter="(sAMAccountName={0})" user-search-base="dc=myDomain,dc=com" cache-ref="userCache" />
</authentication-provider>
</authentication-manager>
I don't think Spring offers client side LDAP caching out of the box, as caching LDAP query results on the client would pose a security risk. The cache will certainly hold stale data at some point, which is not a huge problem if it's e.g. the email/home address of the user, but much worse when it comes to e.g. role assignments and other authentication/authorization related data. You will be much better off by scaling up the server side, so that it's able to handle the load.
That's being said, introducing caching is pretty easy since Spring 3.1, because it provides excellent support for it. In your case it would be enough to use a custom LdapContextSource
like the following:
public class CachingLdapContextSource extends AbstractContextSource {
@Override
protected DirContext getDirContextInstance(Hashtable environment)
throws NamingException
{
InitialLdapContext context = new InitialLdapContext(environment, null);
return new CachingDirContextWrapper(context);
}
}
The wrapper class simply delegates all DirContext
methods to the underlying implementation and decorates methods to be cached with @Cacheable
.
class CachingDirContextWrapper implements DirContext {
private final DirContext delegate;
CachingDirContextWrapper(DirContext delegate) {
this.delegate = delegate;
}
@Override
@Cacheable(value = "search")
public NamingEnumeration<SearchResult> search(...)
{
return delegate.search(name, matchingAttributes, attributesToReturn);
}
...
}
Refer to the official documentation, and this tutorial on details about how to configure a cache storage to be used by Spring.
But once again, you'd better not do this, I think.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With