Add Spring Boot Example
Closes gh-558
This commit is contained in:
23
samples/boot/build.gradle
Normal file
23
samples/boot/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.6.3'
|
||||
id 'io.spring.dependency-management'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-ldap'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.security:spring-security-ldap'
|
||||
implementation 'com.unboundid:unboundid-ldapsdk'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.springframework.security:spring-security-test'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
31
samples/boot/readme.md
Normal file
31
samples/boot/readme.md
Normal file
@@ -0,0 +1,31 @@
|
||||
A Hello World Spring LDAP application using Spring Boot
|
||||
|
||||
The application is protected by Spring Security and uses an embedded UnboundID container for its LDAP server.
|
||||
|
||||
You can authenticate with HTTP basic using `bob`/`bobspassword`:
|
||||
|
||||
```bash
|
||||
curl --user bob:bobspassword localhost:8080
|
||||
```
|
||||
|
||||
And you should see the response:
|
||||
|
||||
```bash
|
||||
Hello, bob
|
||||
```
|
||||
|
||||
Also, you can hit the `cn` endpoint which uses `LdapTemplate` to query the datastore for the user's `cn` attribute value, like so:
|
||||
|
||||
```bash
|
||||
curl --user bob:bobspassword localhost:8080/cn
|
||||
```
|
||||
|
||||
This should result in:
|
||||
|
||||
```bash
|
||||
[
|
||||
"Bob Hamilton"
|
||||
]
|
||||
```
|
||||
|
||||
To run the example, do `./gradlew :bootRun`.
|
||||
27
samples/boot/src/main/java/sample/HelloController.java
Normal file
27
samples/boot/src/main/java/sample/HelloController.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package sample;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class HelloController {
|
||||
@Autowired
|
||||
LdapTemplate ldap;
|
||||
|
||||
@GetMapping
|
||||
public String hello(Authentication authentication) {
|
||||
return "Hello, " + authentication.getName();
|
||||
}
|
||||
|
||||
@GetMapping("/cn")
|
||||
public List<String> cn(Authentication authentication) {
|
||||
AttributesMapper<String> mapper = (attrs) -> attrs.get("cn").get().toString();
|
||||
return this.ldap.search("ou=people", "uid=" + authentication.getName(), mapper);
|
||||
}
|
||||
}
|
||||
42
samples/boot/src/main/java/sample/SecurityConfig.java
Normal file
42
samples/boot/src/main/java/sample/SecurityConfig.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package sample;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticator;
|
||||
import org.springframework.security.ldap.server.UnboundIdContainer;
|
||||
import org.springframework.security.ldap.userdetails.PersonContextMapper;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
@Bean
|
||||
UnboundIdContainer ldapContainer() {
|
||||
UnboundIdContainer container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:users.ldif");
|
||||
container.setPort(0);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ContextSource contextSource(UnboundIdContainer container) {
|
||||
int port = container.getPort();
|
||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@Bean
|
||||
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
|
||||
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
|
||||
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
|
||||
LdapAuthenticationProvider provider = new LdapAuthenticationProvider(authenticator);
|
||||
provider.setUserDetailsContextMapper(new PersonContextMapper());
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SpringLdapSimpleSampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringLdapSimpleSampleApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
124
samples/boot/src/main/resources/users.ldif
Normal file
124
samples/boot/src/main/resources/users.ldif
Normal file
@@ -0,0 +1,124 @@
|
||||
dn: ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: groups
|
||||
|
||||
dn: ou=subgroups,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: subgroups
|
||||
|
||||
dn: ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: people
|
||||
|
||||
dn: ou=space cadets,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: space cadets
|
||||
|
||||
dn: ou=\"quoted people\",dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: "quoted people"
|
||||
|
||||
dn: ou=otherpeople,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: otherpeople
|
||||
|
||||
dn: uid=ben,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Ben Alex
|
||||
sn: Alex
|
||||
uid: ben
|
||||
userPassword: $2a$10$c6bSeWPhg06xB1lvmaWNNe4NROmZiSpYhlocU/98HNr2MhIOiSt36
|
||||
|
||||
dn: uid=bob,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Bob Hamilton
|
||||
sn: Hamilton
|
||||
uid: bob
|
||||
userPassword: bobspassword
|
||||
|
||||
dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Joe Smeth
|
||||
sn: Smeth
|
||||
uid: joe
|
||||
userPassword: joespassword
|
||||
|
||||
dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Mouse, Jerry
|
||||
sn: Mouse
|
||||
uid: jerry
|
||||
userPassword: jerryspassword
|
||||
|
||||
dn: cn=slash/guy,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: slash/guy
|
||||
sn: Slash
|
||||
uid: slashguy
|
||||
userPassword: slashguyspassword
|
||||
|
||||
dn: cn=quote\"guy,ou=\"quoted people\",dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: quote\"guy
|
||||
sn: Quote
|
||||
uid: quoteguy
|
||||
userPassword: quoteguyspassword
|
||||
|
||||
dn: uid=space cadet,ou=space cadets,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: Space Cadet
|
||||
sn: Cadet
|
||||
uid: space cadet
|
||||
userPassword: spacecadetspassword
|
||||
|
||||
|
||||
|
||||
dn: cn=developers,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfUniqueNames
|
||||
cn: developers
|
||||
ou: developer
|
||||
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
|
||||
uniqueMember: uid=bob,ou=people,dc=springframework,dc=org
|
||||
|
||||
dn: cn=managers,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfUniqueNames
|
||||
cn: managers
|
||||
ou: manager
|
||||
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
|
||||
uniqueMember: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
|
||||
|
||||
dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfUniqueNames
|
||||
cn: submanagers
|
||||
ou: submanager
|
||||
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
|
||||
@@ -0,0 +1,40 @@
|
||||
package sample;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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.HttpHeaders;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class SpringLdapSimpleSampleApplicationTests {
|
||||
|
||||
@Autowired
|
||||
MockMvc mvc;
|
||||
|
||||
@Test
|
||||
void indexWhenCorrectUsernameAndPasswordThenAuthenticates() throws Exception {
|
||||
HttpHeaders http = new HttpHeaders();
|
||||
http.setBasicAuth("bob", "bobspassword");
|
||||
this.mvc.perform(get("/").headers(http))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("Hello, bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cnWhenCorrectUsernameAndPasswordThenShowsCommonName() throws Exception {
|
||||
HttpHeaders http = new HttpHeaders();
|
||||
http.setBasicAuth("bob", "bobspassword");
|
||||
this.mvc.perform(get("/cn").headers(http))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.[0]").value("Bob Hamilton"));
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ include 'test/integration-tests-sunone'
|
||||
include 'test/integration-tests-ad'
|
||||
include 'samples/plain'
|
||||
include 'samples/odm'
|
||||
include 'samples/boot'
|
||||
|
||||
|
||||
rootProject.children.each { p->
|
||||
|
||||
Reference in New Issue
Block a user