Spring Security 3.0 with Active Directory LDAP redux…plus remember me functionality

A while back, I released my spring security configuration for doing Spring Security with Active Directory. I have since made a few changes to my configuration that has simplified it and I also got the remember me functionality working. For those of you who don’t know, remember me is a way to tell the application to remember a user for a given length of time and expire when that time is up to make them relogin.

The remember me functionality came in real handy for us since we have so much Ajax going on. It was a pain for users when they would time out after 15 minutes and immediately get redirected to the login page. I also made my configuration utilize a database to store the saved user data instead of allowing it to create everything in a cookie on the user’s machine which is extremely un-secure. I use this technique in two applications. One of them uses an in-memory database mapped with hibernate and the other uses a physical Oracle database. In order to make the PersistentToken storage for Spring 3.0 work, you need to create a table. Below the configuration is a hibernate class you can use to automatically create the table for you. I mapped the table with hibernate so that I could create a screen that tracks all logged in users, when they logged in, etc. It also gives me the ability to play God and log people out just by deleting their user record.

Another reason I chose the Persistent token mechanism is that I couldn’t get the plain token mechanism to work.

Well enough of my chatter and let’s look at my updated Active Directory and LDAP configuration with Spring 3.0. Take note that this configuration is a hybrid configuration between namespace and bean configurations. The key elements to making this configuration sing are in bold.

<beans xmlns="http://www.springframework.org/schema/beans&quot;
xmlns:context=”http://www.springframework.org/schema/context&#8221;
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:aop=”http://www.springframework.org/schema/aop&#8221;
xmlns:security=”http://www.springframework.org/schema/security&#8221;
xmlns:tx=”http://www.springframework.org/schema/tx&#8221;
xsi:schemaLocation=”
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd”&gt;

<security:global-method-security
secured-annotations=”enabled” jsr250-annotations=”enabled”
pre-post-annotations=”enabled” />

<security:http use-expressions=”true” auto-config=’false’
realm=”project” entry-point-ref=”authenticationProcessingFilterEntryPoint”>

<security:intercept-url pattern="/login.jsp*"
filters=”none” />

<security:intercept-url pattern="/logout.jsp*"
filters=”none” />

<security:form-login login-page='/login.jsp'
default-target-url=’/index.jsp’ always-use-default-target=’true’ />

<!–
<security:intercept-url pattern="/*.jsp"
access=”hasRole(‘ROLE_WEB_PROMISES_INQUIRY’)” />
–>

<security:intercept-url pattern="/portal.jsp"
access=”hasAnyRole(‘ROLE_USER’,'ROLE_iArchiveViewer_Backoffice’,'ROLE_SUPPORT’)” />

<security:intercept-url pattern="/"
access=”hasAnyRole(‘ROLE_USER’,'ROLE_iArchiveViewer_Backoffice’,'ROLE_SUPPORT’)” />

<security:intercept-url pattern="/index.jsp"
access=”hasAnyRole(‘ROLE_USER’,'ROLE_iArchiveViewer_Backoffice’,'ROLE_SUPPORT’)” />

<security:intercept-url pattern="/rest/**"
access=”hasAnyRole(‘ROLE_USER’,'ROLE_iArchiveViewer_Backoffice’,'ROLE_SUPPORT’)” />

<security:intercept-url pattern="/RPCAdapter/**"
access=”hasAnyRole(‘ROLE_USER’,'ROLE_iArchiveViewer_Backoffice’,'ROLE_SUPPORT’)” />

<!–
<security:intercept-url pattern="/rest/transaction/payment/cancel/**"
access=”hasRole(‘ROLE_WEB_PROMISES_INQUIRY’)” />
–>

<!– –>

<!–
<concurrent-session-control max-sessions="1"
exception-if-maximum-exceeded=”true”/>
–>

<!– –>

<!– If you want to use basic authentication–>

<!–
Used if access is denied to the application. errorPage = the page to
display if access is denied.
–>
<bean id="accessDeniedHandler"
class=”org.springframework.security.web.access.AccessDeniedHandlerImpl”>

<bean id="authenticationProcessingFilterEntryPoint"
class=”org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint”>

<!–
============================== LDAP Definitions
======================================
–>

<!–
constructor = LDAP domain name userDn = user id used to query the
directory password = password for managerDn baseEnvironmentProperties
= additional ldap environment properties
–>
<bean id="initialDirContextFactory"
class=”org.springframework.security.ldap.DefaultSpringSecurityContextSource”>
<constructor-arg
value=”ldap://domain:389/dc=domain,dc=com” />
<property name="userDn"
value=”CN=projectldap,OU=Service Accounts,OU=Organizational Units,DC=domain,DC=com” />

follow

<!–
Used to search active directory for the user id of the user attempting
to login to the system. 1. Instead of binding directly, Acegi uses the
filter to find a matching user 2. If no user is found, that’s a
failure. 3. If a user is found, takes that users DN and tries to bind
using it 4. Success to bind means that we are okay, failure means
incorrect password constructor 0 = BaseDN for user search constructor
1 = Filter statement for user id lookup constructor 2 = initial
context factory defined above searchSubtree = search the subtrees
beneath BaseDN.
–>
<bean id="userSearch"
class=”org.springframework.security.ldap.search.FilterBasedLdapUserSearch”>

<!–
Peform the LDAP user bind using the context factory and user search

constructor = initial context factory defined above userSearch =
userSearch defined above
–>
<bean id="bindAuthenticator"
class=”org.springframework.security.ldap.authentication.BindAuthenticator”>

<!–
Used for authorization. Populate the user with a set of roles based
upon AD groups. constructor 0 = context factory constructor 1 = BaseDN
for groups groupRoleAttribute = attribute used for role names
–>
<bean id="authoritiesPopulator"
class=”org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator”>

<!–
************************** Athentication
************************************
–>

<!–
LDAP Authentication Provider uses the bindAuthenticator and
authoritiesPopulator to authenticate the user and populate the user
object with roles based upon Active Directory groups.
–>
<bean id="ldapAuthProvider"
class=”org.springframework.security.ldap.authentication.LdapAuthenticationProvider”>

<bean id="userDetailsContextMapper"
class=”com.domain.acegi.AttributesLDAPUserDetailsContextMapper”>

description

ROLE_USER
ROLE_iArchiveViewer_Backoffice
ROLE_SUPPORT

<bean id="userDetailsService"
class=”org.springframework.security.ldap.userdetails.LdapUserDetailsService”>

<security:authentication-provider
ref=”ldapAuthProvider” user-service-ref=”userService” />

Here is the Hibernate class I use to create the table for the Persistent token mechanism.

import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.bbvacompass.domain.DataObject;

@Entity
@Table(name="PERSISTENT_LOGINS")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@org.hibernate.annotations.Entity(mutable = false)
public class PersistentLogin extends DataObject implements java.io.Serializable{

private Integer id;
private String userName;

private String series;//Primary Key
private String token;
private Timestamp lastUsed;

@Override
@Column(name="ID")
public Integer getId() {

return id;
}

@Override
public void setId(Integer id) {
this.id = id;

}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

@Id
public String getSeries() {
return series;
}

public void setSeries(String series) {
this.series = series;
}

public String getToken() {
return token;
}

public void setToken(String token) {
this.token = token;
}

@Column(name="LAST_USED")
public Timestamp getLastUsed() {
return lastUsed;
}

public void setLastUsed(Timestamp lastUsed) {
this.lastUsed = lastUsed;
}

}

I almost forgot one important step. You need to pass the correct paramter when a user logs in to trigger the remember me. I use a hidden field, because we didn’t want users choosing the option. It will be automatic. Remember the parameter must be _spring_security_remember_me=on or it will not work. The following code is in the login form I use.

Also, don’t forget to provide a logout button somewhere or the user will never be able to log out until the cookie expires and the above configuration is for two weeks.

About these ads

About Chris Hardin
Chief Architect at Doozer Software in Birmingham, Al. I specialize in Java and .NET Architecture, Ajax Frameworks and Mobile Architecture with iOS and Android.

9 Responses to Spring Security 3.0 with Active Directory LDAP redux…plus remember me functionality

  1. Jai says:

    Hi Chris,
    I gone through your post. I am new to Spring. I want Spring security to handle two apps as if user login in one application the second application does not prompt for login screen but it automatically goes to the home page of the second application and vice versa. Can you please explain how to implement this step by step or please show sample running application.

    Thanks
    Jai

  2. Chris Hardin says:

    that's easy, just do exactly what I have in this post in both apps and then make sure the remember me key is the same. security:remember-me key=”CommonKey” token-validity-seconds=”864000″

  3. Jai says:

    Hi Chris,

    If i implemented the above configurations in my applicationContext-security.xml the application is not running. I am using spring 2 with security. Is it possible that in my application can i able to implement session sharing between application only by adding security:remember-me key=”CommonKey” and make the username available for other application running in the same brower. If yes whether userDetails are available for the second app.(Since we are not using login screen which spring handles the authentication.

    It must be helpful if you help me in this.

    Thanks
    Jai

  4. Jai says:

    Hi Chris,
    I had used tutorial sample application for my testing. Now i had two applications in my webapp such as tutorial1, tutorial2.
    In both the application i replaced the following line in applicationContext-security.xml file.
    remember-me key=”CommonKey” token-validity-seconds=”864000″

    If i run tutorial1 it shows login page and after entering username and password and selected the rememberme check box and clicked submit button. Now it goes to home page. If i open new tab and access the same tutorial1 application it automatically goes to home page without asking for login.
    But if i access tutorial2 application it doesn't share the session/cookie but prompts for username and password. Why?

    Am i doing anything wrong?Please help me in this.

    Thanks
    Jai

  5. Jai says:

    Chris,

    I had created two sample webapps with remember-me key=”CommonKey” token-validity-seconds=”864000″. Both applications resides separately in tomcat 5.0 server. In my login.jsp page i included the hidden field for remember me in both the applications. If i run the first application it prompts for login page. After successful login it goes to the app1 home page.
    After that if i run my second application app2 it also prompts for the login page but if i run the first application second time it directly redirect to the home page which is correct. I need the same behaviour for the second application also. But i can't able to do it until i login separately for application2. I had googled for past three days but with no solution. Can you tell what am i missing inorder to get the remember me cookie set by the first application from the second.

    Thanks
    Jai

  6. Jai says:

    Hi Chris,

    I had implemented two sample application with remember-me functionality. Everything working fine if i run the application separately and check for the cookie. But if i run first application it prompts for the username and password and sets the cookie. Now if i run the second application it doesnt use the cookie set by the first application but again ask for username and password for that application. But i need the second application to go to the home page directly. how to acieve this? Am i missing any thing. What have to include in my application?

    Thanks
    Jai

  7. djKianoosh says:

    Are you sure you mean “spring security 3.0″, as in the 3.0.0.RC2 release candidate and not the spring security 2.0.5 version? Just double checking which version you are using.

  8. Anonymous says:

    do we need sping-ldap.jar while using spring security 3.0 ?
    i am using with spring 3.0 + spring security 3.1

    i am getting exception class not found
    java.lang.NoClassDefFoundError: org/springframework/ldap/core/support/LdapContextSource

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: