You will create a custom Spring Boot Starter using the auto-configuration features.
Time: 30 minutes.
Spring Boot Starter
We are going create custom Spring Boot Starter. This starter will be a reusable client for the directory-web-security project. It will have the following features:
-
DirectoryWebClient bean that will have access to the persons in the directory. You will add a new person or list all the persons.
-
@EnableDirectoryWebClientUtils annotation that will bring a DirectoryWebClientUtils that will expose password encode using either BCRYPT (default) or PBKDF2 algorthims.
Project Structure
-
Create a directory structure where we are going to place 3 apps:
-
demo-starter
-
directory-web-client-spring-boot-autoconfigure
-
directory-web-client-spring-boot-starter
mkdir -p workspace/demo-starter workspace/directory-web-client-spring-boot-autoconfigure workspace/directory-web-client-spring-boot-starter
-
-
In the workspace directory add the following pom.xml. This will hold all the module information and the general dependencies.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>directory-web-client</name> <modules> <module>directory-web-client-spring-boot-autoconfigure</module> <module>directory-web-client-spring-boot-starter</module> <module>demo-starter</module> </modules> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
As you can see it has the main dependency right now spring-boot-dependencies:2.0.0.M6 and is a pom import. It defines our 3 modules.
Directory Web Client Spring Boot Starter
It’s a good idea to create custom starter by leaving at the end the keywords *-spring-boot-starter and at the beginning the technology that you are going to build.
-
In the directory workspace/directory-web-client-spring-boot-starter add the following pom.xml file:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>directory-web-client-spring-boot-starter</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <parent> <!-- <1> --> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>..</relativePath> </parent> <dependencies> <dependency> <groupId>io.pivotal.workshop</groupId> <!-- <2> --> <artifactId>directory-web-client-spring-boot-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> </project>
-
See that we are using our parent pom.
-
We are adding our auto-configuration app.
Thats it, the spring-boot-starter is just a pom.xml file that declares the auto-configuration dependency.
-
Directory Web Client Spring Boot Autoconfigure
This project will have all the logic to be a reusable component in any project.
-
Open a browser and hit the url: http://start.spring.io
-
Click the Switch to the full version link.
-
Fill out the Directory Web Client Spring Boot Autoconfigure Project metadata with (See Figure 1.0):
Table 1. Directory Web Client Spring Boot Autoconfigure - metadata Property Value Group:
io.pivotal.workshop
Artifact:
directory-web-client-spring-boot-autoconfigure
Name:
directory-web-client-spring-boot-autoconfigure
Package Name:
io.pivotal.workshop.directory
Dependencies:
Web, HATEOAS, Security, Lombok, Configuration Processor
Project Type:
Maven
Spring Boot:
2.0.0.M7
Figure 1.0: Spring Initializr - http://start.spring.io -
Type Web, HATEOAS, Security, Lombok and Configuration Processor in the Dependencies field and press Enter.
-
Click the Generate Project button.
-
IMPORTANT Unzip the file in any directory you want and COPY the content in the workspace/directory-web-client-spring-boot-autoconfigure directory.
-
Import your project in any IDE you want.
-
Modify the pom.xml and make sure it looks like this:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client-spring-boot-autoconfigure</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>directory-web-client-spring-boot-autoconfigure</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <parent> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>..</relativePath> </parent> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.hateoas</groupId> <artifactId>spring-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
-
Let’s start by telling Spring Boot where to go for the auto-configuration. Create in the src/main/resources directory, a new folder named: META-INF and inside this folder a file named: spring.factories with the following content:
src/main/resources/META-INF/spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=io.pivotal.workshop.directory.configuration.DirectoryWebClientAutoConfiguration
Here we are declaring the class that will do the auto-configuration, in this case the io.pivotal.workshop.directory.configuration.DirectoryWebClientAutoConfiguration class.
-
Next let’s create the io.pivotal.workshop.directory.configuration.DirectoryWebClientAutoConfiguration class:
src/main/java/io/pivotal/workshop/directory/configuration/DirectoryWebClientAutoConfiguration.javapackage io.pivotal.workshop.directory.configuration; import io.pivotal.workshop.directory.client.DirectoryWebClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.hateoas.Resource; import org.springframework.web.client.RestTemplate; //(1) @Configuration @ConditionalOnClass({Resource.class,RestTemplateBuilder.class}) @EnableConfigurationProperties(DirectoryWebClientProperties.class) public class DirectoryWebClientAutoConfiguration { private final Logger log = LoggerFactory.getLogger(DirectoryWebClientAutoConfiguration.class); private DirectoryWebClientProperties webClientProperties; private RestTemplateBuilder restTemplateBuilder; //(2) public DirectoryWebClientAutoConfiguration(DirectoryWebClientProperties webClientProperties, RestTemplateBuilder restTemplateBuilder) { this.webClientProperties = webClientProperties; this.restTemplateBuilder = restTemplateBuilder; } //(3) @Bean public DirectoryWebClient directoryClient(){ log.info("Creating a Directory Web Client..."); return new DirectoryWebClient(restTemplate(),this.webClientProperties); } //(4) @Bean public RestTemplate restTemplate(){ // Make sure the directory-web-security has the httpBasic() security enabled and NOT the formLogin() log.info("Setting up admin credentials for Directory Web Client ..."); return this.restTemplateBuilder.basicAuthorization(webClientProperties.getUsername(),webClientProperties.getPassword()).build(); } }
-
This class is annotated with the @ConditionalOnClass that checks if in the classpath is the Resource and the RestTemplateBuilder classes, and if they do, then it will configure all the beans declared in this class.
-
The constructor injects the DirectoryWebClientProperties and the RestTemplateBuilder. The DirectoryWebClientProperties will bring the username and password (the admin credentials for the directory-web-security app.), and the RestTemplateBuilder will create the RestTemplate instance, needed for do all the rest operations.
-
The directoryClient is the one created to do all the rest operations over the directory-web-security app endpoints.
-
This is the RestTeamplate that holds the administrator credentials so it can do all the Rest operations.
TipIs important to make sure the directory-web-security has the httpBasic() security enabled and NOT the formLogin() -
-
Create the io.pivotal.workshop.directory.configuration.DirectoryWebClientProperties class. This class will have the admin credentials and the uri of the directory-web-security app.:
src/main/java/io/pivotal/workshop/directory/configuration/DirectoryWebClientProperties.javapackage io.pivotal.workshop.directory.configuration; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.Arrays; import java.util.List; @Data @ConfigurationProperties(prefix = "directory.web.client") public class DirectoryWebClientProperties { String username = "admin"; String password = "admin"; String uri = "http://localhost:8585/api/persons"; }
See that we are using the Lombok library (with the @Data annotation)so it can generate the setters and getters from the fields.
-
Next, let’s create the actual DirectoryWebClient class that will do all the Rest operations. Create the io.pivotal.workshop.directory.client.DirectoryWebClient class:
src/main/java/io/pivotal/workshop/directory/client/DirectoryWebClient.javapackage io.pivotal.workshop.directory.client; import io.pivotal.workshop.directory.configuration.DirectoryWebClientProperties; import io.pivotal.workshop.directory.domain.Directory; import io.pivotal.workshop.directory.domain.Person; import org.springframework.core.ParameterizedTypeReference; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import java.util.Collection; import java.util.Collections; import static java.net.URI.create; public class DirectoryWebClient { private RestTemplate restTemplate; private DirectoryWebClientProperties props; public DirectoryWebClient(RestTemplate restTemplate, DirectoryWebClientProperties props){ this.restTemplate = restTemplate; this.props = props; } public Person add(Person person){ ResponseEntity<Resource<Person>> response = this.restTemplate.exchange( RequestEntity.post( create(this.props.getUri())) .body(person) ,new ParameterizedTypeReference<Resource<Person>>() {}); return response.getBody().getContent(); } public Collection<Person> getAll() { ResponseEntity<Resource<Directory>> responseEntity = this.restTemplate.exchange(RequestEntity.get(create(this.props.getUri())) .accept(MediaTypes.HAL_JSON) .build(),new ParameterizedTypeReference<Resource<Directory>>() {}); if(responseEntity.getStatusCode() == HttpStatus.OK) { Directory company = responseEntity.getBody().getContent(); return company.getEmbedded().getPersons(); }else return Collections.emptyList(); } }
See that it has (for now) two methods. To add a new person object to the directory and get all the persons in the directory. As you can see, is using a Directoy and Person domain classes.
-
Create the necessary domains, the io.pivotal.workshop.directory.domain.Directory and io.pivotal.workshop.directory.domain.Person classes, you can copy the Person class from the directory-web-security app, without the JPA annotations:
src/main/java/io/pivotal/workshop/directory/domain/Directory.javapackage io.pivotal.workshop.directory.domain; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.List; @Data @JsonIgnoreProperties(ignoreUnknown = true) public class Directory { @JsonProperty("_embedded") public Embedded embedded; @Data public class Embedded { public List<Person> persons; } }
src/main/java/io/pivotal/workshop/directory/domain/Person.javapackage io.pivotal.workshop.directory.domain; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @JsonIgnoreProperties(ignoreUnknown = true) public class Person { private SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd"); private String id; private String email; private String name; private String password; private String role = "USER"; private boolean enabled = true; private Date birthday; private Date created; private Date modified; public Person() { this.created = new Date(); this.modified = new Date(); } public Person(String email, String name, String password, String birthday) { this(); this.email = email; this.name = name; this.password = password; try { this.birthday = date.parse(birthday); } catch (ParseException e) { this.birthday = null; } } public Person(String email, String name, String password, Date birthday) { this(); this.email = email; this.name = name; this.password = password; this.birthday = birthday; } public Person(String email, String name, String password, String birthday, String role, boolean enabled) { this(email, name, password, birthday); this.role = role; this.enabled = enabled; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getCreated() { return created; } public Date getModified() { return modified; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } @Override public String toString() { return "Person{" + "id='" + id + '\'' + ", email='" + email + '\'' + ", name='" + name + '\'' + ", password='" + password + '\'' + ", role='" + role + '\'' + ", enabled=" + enabled + ", birthday=" + birthday + ", date=" + date + ", created=" + created + ", modified=" + modified + '}'; } }
-
Lets add the next feature, the @EnableDirectoryWebClientUtils that will create a DirectoryWebClientUtils bean depending of what algorithm was declared. Add the custom annotation io.pivotal.workshop.directory.annotation.EnableDirectoryWebClientUtils class and its dependency, the io.pivotal.workshop.directory.annotation.Algorithm enum:
src/main/java/io/pivotal/workshop/directory/annotation/EnableDirectoryWebClientUtils.javapackage io.pivotal.workshop.directory.annotation; import io.pivotal.workshop.directory.utils.DirectoryWebClientUtilsConfiguration; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(DirectoryWebClientUtilsConfiguration.class) public @interface EnableDirectoryWebClientUtils { Algorithm algorithm() default Algorithm.BCRYPT; }
src/main/java/io/pivotal/workshop/directory/annotation/Algorithm.javapackage io.pivotal.workshop.directory.annotation; public enum Algorithm { BCRYPT, PBKDF2 }
-
Next let’s create the configuration needed to create the DirectoryWebClientUtils bean. Create the io.pivotal.workshop.directory.utils.DirectoryWebClientUtilsConfiguration class. This class will implement the ImportSelector interface that will be picked up by Spring Boot during the auto-configuration. It will identify if the @EnableDirectoryWebClientUtils was declared in a @Configuration class and it will create the necessary beans.
src/main/java/io/pivotal/workshop/directory/utils/DirectoryWebClientUtilsConfiguration.javapackage io.pivotal.workshop.directory.utils; import io.pivotal.workshop.directory.annotation.Algorithm; import io.pivotal.workshop.directory.annotation.EnableDirectoryWebClientUtils; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; public class DirectoryWebClientUtilsConfiguration implements ImportSelector { public String[] selectImports(AnnotationMetadata annotationMetadata) { AnnotationAttributes attributes = AnnotationAttributes.fromMap( annotationMetadata.getAnnotationAttributes(EnableDirectoryWebClientUtils.class.getName(), false)); Algorithm algorithm = attributes.getEnum("algorithm"); switch(algorithm){ case PBKDF2: return new String[] {"io.pivotal.workshop.directory.utils.Pbkdf2Encoder"}; case BCRYPT: default: return new String[] {"io.pivotal.workshop.directory.utils.BcryptEncoder"}; } } }
The DirectoryWebClientUtilsConfiguration will decide which class to configure based on the Algorithm selected.
-
Lets create the io.pivotal.workshop.directory.utils.BCryptEncoder and io.pivotal.workshop.directory.utils.Pbkdf2Encoder configuration classes. These classes will declare the DirectoryWebClientUtils bean and it will have the Algorithm selected.
src/main/java/io/pivotal/workshop/directory/utils/BCryptEncoder.javapackage io.pivotal.workshop.directory.utils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration public class BCryptEncoder { @Bean public DirectoryWebClientUtils utils(){ return new DirectoryWebClientUtils(new BCryptPasswordEncoder(16)); } }
src/main/java/io/pivotal/workshop/directory/utils/Pbkdf2Encoder.javapackage io.pivotal.workshop.directory.utils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; @Configuration public class Pbkdf2Encoder { @Bean public DirectoryWebClientUtils utils(){ return new DirectoryWebClientUtils(new Pbkdf2PasswordEncoder()); } }
-
Next, create the io.pivotal.workshop.directory.utils.DirectoryWebClientUtils class that will be the bean created based on the Algorithm selected:
src/main/java/io/pivotal/workshop/directory/utils/DirectoryWebClientUtils.javapackage io.pivotal.workshop.directory.utils; import lombok.Data; import org.springframework.security.crypto.password.PasswordEncoder; @Data public class DirectoryWebClientUtils { private PasswordEncoder encoder; public DirectoryWebClientUtils(PasswordEncoder encoder){ this.encoder = encoder; } }
Demo Starter
This project will test the custom spring-boot-starter.
-
Open a browser and hit the url: http://start.spring.io
-
Click the Switch to the full version link.
-
Fill out the Demo Starter Project metadata with (See Figure 1.0):
Table 2. Demo Starter - metadata Property Value Group:
io.pivotal.workshop
Artifact:
demo-starter
Name:
demo-starter
Package Name:
io.pivotal.workshop
Project Type:
Maven
Spring Boot:
2.0.0.M7
Figure 1.0: Spring Initializr - http://start.spring.io -
This is just a simple Spring Boot app, without any additional Dependencies from the Spring Initializr.
-
Click the Generate Project button.
-
IMPORTANT Unzip the file in any directory you want and COPY the content in the workspace/demo-starter directory.
-
Import the project into your favorite IDE.
-
Add the directory-web-client-spring-boot-starter dependency to your pom.xml:
<dependency> <groupId>io.pivotal.workshop</groupId> <artifactId>directory-web-client-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
-
In the src/main/resources/application.properties file add the following properties:
directory.web.client.username=admin directory.web.client.password=admin directory.web.client.uri=http://localhost:8585/api/persons
These are the defaul values, but this give us more flexibility if the directory-web-security app has different credentials or is running in a different port.
-
Next, we are ready to start testing. Make sure you have the directory-web-security running in the port 8585.
Testing the DirectoryWebClient bean
-
In your demo-starter project open the DemoStarterApplication class and add the following code:
package io.pivotal.workshop; import io.pivotal.workshop.directory.client.DirectoryWebClient; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class DemoStarterApplication { public static void main(String[] args) { SpringApplication.run(DemoStarterApplication.class, args); } @Bean ApplicationRunner getPersons(DirectoryWebClient client){ return args -> { client.getAll().forEach(System.out::println); }; } }
In the above code we are using the DirectoryWebClient client and calling the getAll() method.
-
Run the application and you should see all the persons that are in the directory-web-security app.
Tip
|
If you are using the command: ./mvnw spring-boot:run remember that you need to do a: ./mvnw install in the root folder (workspace), so the directory-web-client-spring-boot-starter and its dependencies get installed so they can be used.
|
Testing the @EnableDirectoryWebClientUtils annotation.
-
In your demo-starter project open the DemoStarterApplication class and add the following code:
package io.pivotal.workshop; import io.pivotal.workshop.directory.annotation.Algorithm; import io.pivotal.workshop.directory.annotation.EnableDirectoryWebClientUtils; import io.pivotal.workshop.directory.client.DirectoryWebClient; import io.pivotal.workshop.directory.utils.DirectoryWebClientUtils; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @EnableDirectoryWebClientUtils(algorithm = Algorithm.PBKDF2) @SpringBootApplication public class DemoStarterApplication { public static void main(String[] args) { SpringApplication.run(DemoStarterApplication.class, args); } @Bean ApplicationRunner getPersons(DirectoryWebClient client){ return args -> { client.getAll().forEach(System.out::println); }; } @Bean ApplicationRunner encode(DirectoryWebClientUtils utils){ return args -> { String text = "This text will be encrypted"; String hash = utils.getEncoder().encode(text); System.out.println(">>> ENCRYPT: " + hash); System.out.println(">>> Verify: " + utils.getEncoder().matches(text,hash)); }; } }
-
Run the application and you will see the encrypted text.
CONGRATS! you have created your first custom spring-boot-starter
Challenges
-
Comment out the @EnableDirectoryWebClientUtils, run the app again and see how the it fails because is looking for the DirectoryWebClientUtils bean. That’s the power of creating a @Enable* technology.
-
Add the code to add a new person using the DirectoryWebClient bean.
HOMEWORK
-
Create a Web app that has the CRUD (Create, Read, Up, Delete) Forms for all these actions. It will use the directory-web-client-spring-boot-starter:
-
It will encrypt the user password (TIP: You can use the DirectoryWebClientUtils bean)
-
Add all the missing logic for Update, Delete, etc to the directory-web-client-spring-boot-autoconfigure so it can be more reusable for this new web app.
-