LDAP

Integrate LDAP and Spring Security 3.1 for Authentication and Authorization in a JSF 2.0 Application.

Posted on Updated on

Backtracking all our previous tutorial you will notice we always had our user definition with password placed in our database. Not every time this will be the case and frequently we will have to integrate our application with LDAP. In this tutorial we will integrate our earlier application with Apache Directory Services. If you don’t know how to configure Apache DS then take look at Configuring Apache DS.

In this post we are not implementing anything for remember-me service which we did earlier. Let’s get started with code changes to cater to LDAP Spring Security Integration for both Authentication and Authorization

First We will clean up jsfspring-sec-bean-config.xml as we don’t need anything related to database.

jsfspring-sec-bean-config.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="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">
       
	<beans:bean id="navigator" name="navigator" class="com.mumz.jsfspringsec.beans.Navigator" scope="session">
		<beans:property name="businessModel" ref="businessModel"></beans:property>
	</beans:bean>
	
	<beans:bean id="loginBean" name="loginBean" class="com.mumz.jsfspringsec.beans.LoginBean" scope="prototype">
		<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
	</beans:bean>
	
	<beans:bean id="securityBean" class="com.mumz.jsfspringsec.beans.SecurityHolderBean" scope="session">
	</beans:bean>
	
	<beans:bean id="businessModel" class="com.mumz.jsfspringsec.business.model.BusinessModel" scope="prototype"></beans:bean>
	
	<beans:bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	 	<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<beans:property name="url" value="jdbc:mysql://localhost:3306/jsf-spring-security" />
		<beans:property name="username" value="root" />
		<beans:property name="password" value="root" />
   </beans:bean>
</beans:beans>

Notice that configuration is very simple and all information related to custom database and user details service have been removed.

Second we will update jsfspring-sec-security-config.xml. We will define LdapAuthenticationProvider which uses BindAuthenticator. This won’t work where LDAP is secured and logging to LDAP is disabled. In those cases use PasswordComparisonAuthenticator.

jsfspring-sec-security-config.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans:beans
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:beans="http://www.springframework.org/schema/beans"
	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">
	  
	<sec:http auto-config="true" use-expressions="true">
		<sec:intercept-url pattern="/pages/secure/**" access="hasRole('ROLE_USER')" />
		<sec:intercept-url pattern="/pages/unsecure/**" access="permitAll"/>
		<sec:intercept-url pattern="/pages/common/**" access="permitAll"/>
		<sec:intercept-url pattern="/**" access="permitAll"/>
		<sec:form-login login-page="/pages/common/login.jsf"/>
		<sec:logout 
			invalidate-session="true" 
			delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE" 
			logout-success-url="/pages/common/login.jsf"></sec:logout>
	</sec:http>
	
	<sec:authentication-manager alias="authenticationManager">
		<sec:authentication-provider ref="ldapAuthProvider"></sec:authentication-provider>
	</sec:authentication-manager>
	
	<beans:bean id="ldapContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
  		<beans:constructor-arg value="ldap://localhost:12389/o=mycompany"/>
  		<beans:property name="userDn" value="uid=admin,ou=system"/>
  		<beans:property name="password" value="secret"/>
	</beans:bean>
	
	<beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
 		<beans:constructor-arg>
   			<beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
     			<beans:constructor-arg ref="ldapContextSource"/>
     			<beans:property name="userDnPatterns">
	       			<beans:list>
	       				<beans:value>uid={0},ou=Users</beans:value>
	       			</beans:list>
     			</beans:property>
   			</beans:bean>
 		</beans:constructor-arg>
 		<beans:constructor-arg>
   			<beans:bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
     			<beans:constructor-arg ref="ldapContextSource"/>
     			<beans:constructor-arg value="ou=Groups"/>
     			<beans:property name="groupRoleAttribute" value="cn"/>
   			</beans:bean>
 		</beans:constructor-arg>
	</beans:bean>

	<sec:global-method-security pre-post-annotations="enabled"/>
		
</beans:beans>

Finally our sleek LoginBean.java which just does the login, remember me has been removed.

LoginBean.java

package com.mumz.jsfspringsec.beans;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * The Class LoginBean.
 */
@ManagedBean(name = "loginMgmtBean")
@RequestScoped
public class LoginBean {

	/** The user name. */
	private String					userName				= null;

	/** The password. */
	private String					password				= null;

	/** The remember me. */
	private String					rememberMe				= null;

	/** The authentication manager. */
	@ManagedProperty(value = "#{authenticationManager}")
	private AuthenticationManager	authenticationManager	= null;
	
	/**
	 * Login.
	 * 
	 * @return the string
	 */
	public String login() {
		try {
			Authentication result = null;
			Authentication request = new UsernamePasswordAuthenticationToken(this.getUserName(), this.getPassword());
			result = authenticationManager.authenticate(request);
			SecurityContextHolder.getContext().setAuthentication(result);
		} catch (AuthenticationException e) {
			e.printStackTrace();
		}
		return "Secured";
	}

	/**
	 * Cancel.
	 * 
	 * @return the string
	 */
	public String cancel() {
		return null;
	}

	/**
	 * Logout.
	 * 
	 * @return the string
	 */
	public String logout() {
		SecurityContextHolder.clearContext();
		/**
		 * Delete Cookies
		 */
		HttpServletRequest httpServletRequest = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
		HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext()
				.getResponse();
		Cookie cookie = new Cookie("SPRING_SECURITY_REMEMBER_ME_COOKIE", null);
		cookie.setMaxAge(0);
		cookie.setPath(httpServletRequest.getContextPath().length() > 0 ? httpServletRequest.getContextPath() : "/");
		httpServletResponse.addCookie(cookie);
		return "loggedout";
	}

	/**
	 * Gets the user name.
	 * 
	 * @return the user name
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * Sets the user name.
	 * 
	 * @param userName
	 *            the new user name
	 */
	public void setUserName(String userName) {
		this.userName = userName;
	}

	/**
	 * Gets the password.
	 * 
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Sets the password.
	 * 
	 * @param password
	 *            the new password
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * Gets the remember me.
	 * 
	 * @return the remember me
	 */
	public String getRememberMe() {
		return rememberMe;
	}

	/**
	 * Sets the remember me.
	 * 
	 * @param rememberMe
	 *            the new remember me
	 */
	public void setRememberMe(String rememberMe) {
		this.rememberMe = rememberMe;
	}

	/**
	 * Gets the authentication manager.
	 * 
	 * @return the authentication manager
	 */
	public AuthenticationManager getAuthenticationManager() {
		return authenticationManager;
	}

	/**
	 * Sets the authentication manager.
	 * 
	 * @param authenticationManager
	 *            the new authentication manager
	 */
	public void setAuthenticationManager(AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}
}

That’s all we need for integration Spring Security and LDAP (Apache DS in this case) for both Authorization and Authentication. There is every possibility that LDAP may be used for Authentication and Authorization will be done from database, in another post we will look at one way of implementing such a solution.

References:

  1. Spring Source Documentation

Configuring Apache DS in Eclipse

Posted on Updated on

Lightweight Directory Access Protocol aka LDAP is a protocol used for maintaining data over distributed ENV. Most of the organization will use LDAP for holding there user information and more often then not in almost all our application we will authenticate user against LDAP. In this post we will configure Apache Directory which is built completely in Java and is an open source project of Apache.

First download and install Apache DS

Second install Apache DS plugin from Eclipse Marketplace or using Install New Software option in Eclipse.

Third change your Eclipse Perspective to LDAP

Fourth Right Click under Servers view and New -> New Server and give some name for your server.


Fifth Right click on your server and select LDAP Browser -> Create a connection. You should get a message saying “Connection Created”.


Sixth Double click on your server, this should open server.xml which is the configuration file, change port number of LDAP and LDAPS (since 10389 and 10636 is default port and is used by default server which is part of Apache DS installation).


Seventh Right click on your server and select Run, similarly Right Click on your connection and select Open Connection. Once your connection is open you can expand DIT under LDAP Browser to see the default values.


Eigth Now we will create an organization struture inside our LDAP Server, for that again Double Click on your server this will open up server.xml, in the tabbed pane choose Partitions. Click on Add and provide ID = mycompany, Cache Size = 100 and suffix = o=mycompany
and save.You may have to restart your server and refresh your connection to see updated values.


Ninth Double click on Root DSE this will open up Root DSE editor which will display new Partition that we created.

Tenth Right Click on Root DSE and Select New -> New Context Entry -> Create entry from scratch.

Eleventh choose Organization from Available classes and click on Add.


Twelfth Select o=mycompany as Distinguished Name.


Thirteenth Click on Finish


Fourteenth we will create user and groups under our partition mycompany. To create user right click on mycompany and choose New -> New Entry -> Create entry from scrath.

Fifteenth Select organizationUnit from available classes and click on Add, click on next and then fill in RDN as ou = Groups



Click on Finish and repeat the steps to create organizationUnit for Users

Sixteenth Select Users and then right click select New -> New Entry -> Create Entry from scratch and select inetOrgPerson from available classes and click on Add.


Seventeenth Fill in RDN as uid = testNormalUser and click on Next.


Eighteenth Fill in values for Common Name (cn) and Last Name (sn)


Nineteenth Right click on the editor and choose New Attribute, in the subsequent page type in userPassword and click on Finish



Twentieth Choose Plain Text as Hash Method and type in user password and click on Ok and then click on Finish in user creation page.


Twenty First Right click on Groups and select New -> New Entry -> Create entry from scratch and select groupOfNames, click on Next and then fill in RDN as ou=testNormalGroup.



Twenty Second In DN Editor browse to testNormalUser which we created. Click on OK and then fill in Common Name (cn) for Group and then click finish.




In similar way we can create multiple users and groups. We didn’t use Apache Directory Studio since it is not available for 64 bit machine.