Posted In: Spring, Spring Security

Example – Spring Boot – Security – Integrating With Apache Directory Server LDAP – SHA Password

 

Example shows how to implement login/logout using Apache Directory Server LDAP and Spring Boot. Password is encrypted SHA password. It will be authenticated using LdapShaPasswordEncoder

 

 

1. Create Spring boot project. Refer create-eclipse-spring-boot-application-step-by-step

 
 

2. Choose LDAP checkbox or add following maven entry

<dependency>
	<groupId>org.springframework.ldap</groupId>
	<artifactId>spring-ldap-core</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.security</groupId>
	<artifactId>spring-security-ldap</artifactId>
</dependency>

 
 

3. Add entry to application.properties

logging.level.org.springframework.web=DEBUG
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

 
 

4a. Download and install Apache Directory Server

apacheds

Start AacheDS Service


 
 

4b. Install eclipse Apache Directory LDAP plugin


 
 

4c. Open LDAP perspective


 
 

4d. Create connection


 

 
 

4e. Create partition


 
 

4f. Import LDIF file for test data


 
 

4g. Import LDIF sample test file. Restart the server.

objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: javausecase

dn: ou=groups,dc=javausecase,dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=subgroups,ou=groups,dc=javausecase,dc=com
objectclass: top
objectclass: organizationalUnit
ou: subgroups

dn: ou=people,dc=javausecase,dc=com
objectclass: top
objectclass: organizationalUnit
ou: people

dn: uid=abhijit,ou=people,dc=javausecase,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Abhijit Pednekar
sn: Abhijit
uid: abhijit
userPassword: {SHA}fDYHuOYbzxlE6ehQOmYPIfS28/E=

 
 

5. Use WebSecurityConfigurerAdapter and LdapShaPasswordEncoder to authenticate user

package com.example;

import java.util.Arrays;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;

@Configuration
public class WebSecurityConfig
        extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http)
	        throws Exception {
		System.out.println(
		        "WebSecurityConfig configure(HttpSecurity http)");
		http.authorizeRequests().anyRequest()
		        .fullyAuthenticated().and().formLogin();
	}

	@Override
	public void configure(AuthenticationManagerBuilder auth)
	        throws Exception {
		System.out.println(
		        "WebSecurityConfig configure(AuthenticationManagerBuilder auth)");
		auth.ldapAuthentication()
		        .userDnPatterns("uid={0},ou=people")
		        .groupSearchBase("ou=groups")
		        .contextSource(contextSource()).passwordCompare()
		        .passwordEncoder(new LdapShaPasswordEncoder())
		        .passwordAttribute("userPassword");
	}

	@Bean
	public DefaultSpringSecurityContextSource contextSource() {
		System.out.println(
		        "DefaultSpringSecurityContextSource contextSource()");
		return new DefaultSpringSecurityContextSource(
		        Arrays.asList("ldap://localhost:10389/"),
		        "dc=javausecase,dc=com");
	}

}

 
 

6. Create simple login FORM

Login form is automatically generated by Spring Boot. It will also generate hidden CSRF token by default.
 
 

7. Create Controller

package com.example;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginService {
	@RequestMapping("/")
	public String index(Map<String, Object> model) {
		System.out.println("index() called");
		model = new HashMap<String, Object>();
		return "loginSuccess";
	}

	@RequestMapping(value = "/logout",
	        method = RequestMethod.GET)
	public String logout(HttpServletRequest request,
	        HttpServletResponse response) {
		System.out.println("logout() called");
		SecurityContext securityContext = SecurityContextHolder
		        .getContext();
		Authentication auth = securityContext
		        .getAuthentication();

		System.out.println("auth.isAuthenticated()="
		        + auth.isAuthenticated());
		System.out.println("auth.getName()=" + auth.getName());
		System.out.println(
		        "auth.getPrincipal()=" + auth.getPrincipal());

		if (auth != null) {
			new SecurityContextLogoutHandler().logout(request,
			        response, auth);
		}
		return "redirect:/login";
	}
}

 
 

8. Test

 

8a. Test in browser

 

Use user id abhijit and password abcd123 for testing

 

 

 
 

8b. Test with MockMVC

 

package com.example;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class Example201728ApplicationTests {

	@Autowired
	private MockMvc mvc;

	@Test
	public void contextLoads() {
	}

	@Test
	public void testAuth() throws Exception {

		mvc.perform(MockMvcRequestBuilders.post("/services/login")
		        .accept(MediaType.TEXT_HTML)
		        .contentType(
		                MediaType.APPLICATION_FORM_URLENCODED)
		        .param("username", "abhijit")
		        .param("password", "abcd123").with(csrf()))
		        .andExpect(status().is3xxRedirection());
	}
}

 
 

8c. Test with Rest template

 
As this example is using CSRF token it is little bit tricky to test it through Rest Template. What I have done here is first call /login and parse HTML to get CSRF token. Use this token to pass on to next call.

 

package com.example;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.Test;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

public class Example201728ApplicationTests2 {
	@Test
	public void testBasicAuth2() throws Exception {
		RestTemplate rest = new RestTemplate(
		        new HttpComponentsClientHttpRequestFactory());
		HttpHeaders headers = new HttpHeaders();
		HttpEntity<String> entity = null;
		ResponseEntity<String> response = null;
		String url = "http://localhost:8080/services/login";
		String csrfToken = "";
		System.out.println("1---------------------");
		try {
			rest.getMessageConverters()
			        .add(new StringHttpMessageConverter());
			headers.setContentType(
			        MediaType.APPLICATION_FORM_URLENCODED);

			entity = new HttpEntity<String>("", headers);
			response = rest.exchange(url, HttpMethod.GET, entity,
			        String.class);
			System.out.println(response.getBody());
			Document doc = Jsoup.parse(response.getBody());
			Elements inputs = doc.getElementsByTag("input");
			for (Element input : inputs) {
				if (input.toString().contains("_csrf")) {
					csrfToken = input.val();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.println("2---------------------");
		try {
			rest.getMessageConverters()
			        .add(new StringHttpMessageConverter());
			headers.setContentType(
			        MediaType.APPLICATION_FORM_URLENCODED);

			entity = new HttpEntity<String>(
			        "username=abhijit&password=abcd123&_csrf="
			                + csrfToken,
			        headers);
			response = rest.exchange(url, HttpMethod.POST,
			        entity, String.class);
			System.out.println(response.getBody());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 
 

 
 

Tags:

by , on July 25th, 2017

  • sai jaswanth kalavendi

    i’am having a problem with ldif file. When i’am trying to import ldif
    file in ApacheDS ….. it is saying – [LDAP: error code 16 –
    NO_SUCH_ATTRIBUTE: failed for MessageType : ADD_REQUES
    java.lang.Exception: [LDAP: error code 16 – NO_SUCH_ATTRIBUTE: failed for MessageType : ADD_REQUEST
    Message ID : 15

    • JAVAUSECASE

      Did you create partition as described in section 4e

  • Categories