Boot

An Intro to Spring Cloud Vault

Welcome, this is an intro to Spring Cloud Vault. We will see how to integrate HashiCorp Vault in a spring boot application. Vault is an application that stores secrets and this information is stored in 256AES encrypted form. For this tutorial, we will start the Vault server in development mode.

1. Introduction

Before going further in this tutorial, we will look at the common terminology such as introduction to Spring Boot and Lombok.

1.1 Spring Boot

  • Spring boot is a module that provides rapid application development feature to the spring framework including auto-configuration, standalone-code, and production-ready code
  • It creates applications that are packaged as jar and are directly started using embedded servlet container (such as Tomcat, Jetty or, Undertow). Thus, no need to deploy the war files
  • It simplifies the maven configuration by providing the starter template and helps to resolve the dependency conflicts. It automatically identifies the required dependencies and imports them into the application
  • It helps in removing the boilerplate code, extra annotations, and xml configurations
  • It provides powerful batch processing and manages the rest endpoints
  • It provides an efficient jpa-starter library to effectively connect the application with the relational databases
  • It offers a Microservice architecture and cloud configuration that manages all the application related configuration properties in a centralized manner

1.2 Lombok

  • Lombok is nothing but a small library that reduces the amount of boilerplate Java code from the project
  • Automatically generates the getters and setters for the object by using the Lombok annotations
  • Hooks in via the Annotation processor API
  • Raw source code is passed to Lombok for code generation before the Java Compiler continues. Thus, produces properly compiled Java code in conjunction with the Java Compiler
  • Under the target/classes folder you can view the compiled class files
  • Can be used with Maven, Gradle IDE, etc.

1.2.1 Lombok features

FeatureDetails
valLocal variables are declared as final
varMutable local variables
@Slf4JCreates an SLF4J logger
@CleanupWill call close() on the resource in the finally block
@GetterCreates getter methods for all properties
@SetterCreates setter for all non-final properties
@EqualsAndHashCode
  • Generates implementations of equals(Object other) and hashCode()
  • By default will use all non-static, non-transient properties
  • Can optionally exclude specific properties
@ToString
  • Generates String of class name, and each field separated by commas
  • Optional parameter to include field names
  • Optional parameter to include a call to the super toString method
@NoArgsConstructor
  • Generates no-args constructor
  • Will cause compiler error if there are final fields
  • Can optionally force, which will initialize final fields with 0/false/null var – mutable local variables
@RequiredArgsContructor
  • Generates a constructor for all fields that are final or marked @NonNull
  • The constructor will throw a NullPointerException if any @NonNull fields are null val – local variables are declared final
@AllArgsConstructor
  • Generates a constructor for all properties of the class
  • Any @NotNull properties will have null checks
@Data
  • Generates typical boilerplate code for POJOs
  • Combines – @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor
  • No constructor is generated if constructors have been explicitly declared
@Builder
  • Implements the Builder pattern for object creation
@Value
  • The immutable variant of @Data
  • All fields are made private and final by default

Let us go ahead with the tutorial implementation but before going any further I’m assuming that you’re aware of the Spring boot basics.

2. An Intro to Spring Cloud Vault

2.1 Application Pre-requisite

To start with this tutorial, I am hoping you have the Hashicorp Vault server up and running in your localhost environment. For easy setup, I have the Hashicorp Vault server up and running on the Docker environment. You can execute the below script using the docker-compose command to get the Hashicorp Vault container running on Docker in minutes. If you’re doing it for the first time the vault docker image will be downloaded from the docker hub.

docker-compose.yml

version: '3'
services:
  vault:
    container_name: vault
    image: vault
    ports:
      - '8200:8200'
    environment:
      VAULT_DEV_LISTEN_ADDRESS: '0.0.0.0:8200'
      VAULT_DEV_ROOT_TOKEN_ID: 00000000-0000-0000-0000-000000000000
    cap_add:
      - IPC_LOCK

If everything goes well the vault container would be started successfully as shown in Fig. 1. You can use the docker ps -a command to confirm that the vault container is started successfully. For further information on docker basics, you can navigate to this tutorial.

spring Cloud Vault - Hashicorp Vault
Fig. 1: Hashicorp Vault container on Docker

2.2 Tools Used for Spring boot application and Project Structure

We are using Eclipse Kepler SR2, JDK 8, and Maven. In case you’re confused about where you should create the corresponding files or folder, let us review the project structure of the spring boot application.

spring Cloud Vault - project structure
Fig. 2: Project structure

Let us start building the application!

3. Configure Hashicorp Vault server

Once the vault server is up and running, head over to the administration console by typing the following address in the browser – http://127.0.0.1:8200/ui/. A login page will be displayed where you will enter the token specified in the docker-compose.yml file.

Fig. 3: Enter authentication details

After the successful authentication, you will create the secrets for the spring boot application. In Vault, secrets manage the secrets for an application. As the next steps, navigate to the secrets tab and click on Create secret button as shown in Fig. 4.

Fig. 4: Creating a new secret

Add the application and secret details as shown in Fig. 5 and once done hit the save button.

Fig. 5: Adding secrets for application

4. Creating a Spring Boot application

Below are the steps involved in developing the application.

4.1 Maven Dependency

Here, we specify the dependency for the Spring boot (Web and JPA), Java Faker, H2 database, Lombok, and Spring cloud starter vault config. Maven will automatically resolve the other dependencies. The updated file will have the following code.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.springboot.vault</groupId>
    <artifactId>SpringbootandVault</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringbootandVault</name>
    <description>Springboot and hashicorp vault</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-vault-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>

</project>

4.2 Application yml file

Create a new yml file at the location: SpringbootandVault/src/main/resources/ and add the following code to it. Here we will define the application, h2 database, and spring jpa configuration. The h2 database url, username, and password will be fetched from the Hashicorp vault on the application start by making an API call to the vault.

application.yml

server:
  port: 9500
spring:
  datasource:
    driverClassName: org.h2.Driver
    password: '${h2.db.password}'
    url: '${h2.db.url}'
    username: '${h2.db.username}'
  h2:
    console:
      enabled: true
      path: /h2-console
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        show_sql: true

4.3 Bootstrap yml file

Create a new yml file at the location: SpringbootandVault/src/main/resources/ and add the following code to it. Here we will define the application name (specified while defining the secret as shown in Fig. 5) and the vault configuration.

bootstrap.yml

spring:
  application:
    name: springboot-vault
  cloud:
    vault:
      host: 127.0.0.1
      kv:
        enabled: true
      port: 8200
      scheme: http
      token: 00000000-0000-0000-0000-000000000000

4.4 Java Classes

Let us write the important java class(es) involved in this application. For brevity, we will skip the following classes –

  • Book.java – Model class to map the attributes with the database
  • BeanConfig.java – Configuration class to define the @Bean definition methods
  • BookRepository.java – Interface that extends the CrudRepository to perform the SQL operations
  • BookService.java – Service class that provide some business facilities and interact with the DAO layer methods

4.4.1 Implementation/Main class

Add the following code to the main class to bootstrap the application from the main method. Always remember, the entry point of the spring boot application is the class containing @SpringBootApplication annotation and the static main method.

SpringbootandVaultApplication.java

package com.springboot.vault;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//lombok annotation for logger
@Slf4j
//spring annotation
@SpringBootApplication
public class SpringbootandVaultApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootandVaultApplication.class, args);
        log.info("Springboot and hashicorp vault application successfully started");
    }
}

4.4.2 Bootstrap class

Add the following code to the bootstrap class to populate the dummy data to the database on application startup. For this, we will implement an CommandLineRunner interface that provides the run(…) method. The run(…) method will be executed on application startup. Once the dummy data is persisted in the database, you can use the h2 console – http://localhost:9500/h2-console to view the data. The login id and password for the database can be read from the secret defined in the vault for the application name – springboot-vault. The class is injected with the service dependency whose method will call the DAO layer method to persist the data into the database.

DefaultBookLoader.java

package com.springboot.vault.model.bootstrap;

import com.github.javafaker.Faker;
import com.springboot.vault.model.Book;
import com.springboot.vault.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.Period;

//lombok annotation for logger
@Slf4j
//spring stereotype annotation
@Component
public class DefaultBookLoader implements CommandLineRunner {

    @Autowired
    BookService bookService;
    @Autowired
    Faker faker;

    @Override
    public void run(String... args) {
        loadBooks();
    }

    private void loadBooks() {
    	final long count = bookService.getBooksCount();
    	log.info("Total books present in db are= {}", count);
        if (count==0) {
            log.info("Saving the default books into the db");
            for (int x=0; x<10; x++) {
                persist();
            }
        } else {
            log.info("{} books are already present in the db", count);
        }
    }

    private void persist() {
        final Book b = createNewBook();
        bookService.save(b);
    }

    private Book createNewBook() {
        final int randomNumber = faker.number().numberBetween(50, 100);
        return Book.builder()
                .author(faker.book().author())
                .title(faker.book().title())
                .publisher(faker.book().publisher())
                .genre(faker.book().genre())
                .quantity(randomNumber)
                .publishedOn(LocalDateTime.now().minusHours(randomNumber)
                        .minus(Period.ofWeeks(randomNumber)))
                .build();
    }
}

4.4.3 Controller class

Add the following code to the controller class. The class is injected with the service dependency whose method will call the DAO layer methods to persist the data into the database or fetch from it.

BookController.java

package com.springboot.vault.service.controller;

import com.springboot.vault.model.Book;
import com.springboot.vault.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.List;

// NOTE - We have left the implementation of the HATEOAS principle for simplicity.

//lombok annotation for logger
@Slf4j
//spring annotations
@RestController
@RequestMapping("/api")
public class BookController {

    @Autowired
    BookService bookService;

    // URL - http://localhost:9500/api/books
    @GetMapping("/books")
    @ResponseStatus(HttpStatus.OK)
    public List<Book> getBooks() {
        log.info("Getting all books from the dB");
        return bookService.getAllBooks();
    }

    // URL - http://localhost:9500/api/books/genre/Mythopoeia
    @GetMapping("/books/genre/{genre}")
    @ResponseStatus(HttpStatus.OK)
    public List<Book> getBooksByGenre(@PathVariable(name = "genre") final String genre) {
        log.info("Getting book(s) for genre= {} from the dB", genre);
        return bookService.getAllBooksByGenre(genre);
    }

    // URL - http://localhost:9500/api/book/save
    // Sample HTTP POST request body
    /*
    {
        "author": "Vasdev Mohi",
        "genre": "Ghazals",
        "publisher": "Central Sahitya Akademi",
        "title": "Cheque book",
        "quantity": 1,
        "publishedOn": "2020-09-11T11:11:36Z"
    }
    */
    @PostMapping("/book/save")
    @ResponseStatus(HttpStatus.CREATED)
    public void save(@RequestBody final Book b) {
        log.info("Saving book with details= {} in the dB", b.toString());
        bookService.save(b);
    }
}

5. Run the Application

To execute the application, right-click on the SpringbootandVaultApplication.java class, Run As -> Java Application.

spring Cloud Vault - run the app
Fig. 6: Run the Application

6. Project Demo

When the application is started, open the Postman tool to hit the application endpoints to persist the data into the database or fetch from it. You are free to use any other tool of your choice to make the post and get requests to the endpoints.

Application endpoints

-- HTTP GET endpoint –
http://localhost:9500/api/books

-- HTTP GET endpoint (to fetch books by genre) --
http://localhost:9500/api/books/genre/<book_genre>

-- HTTP POST endpoint (to save a new book into the database) –
http://localhost:9500/api/book/save

-- sample request body –
{
        "author": "Vasdev Mohi",
        "genre": "Ghazals",
        "publisher": "Central Sahitya Akademi",
        "title": "Cheque book",
        "quantity": 1,
        "publishedOn": "2020-09-11T11:11:36Z"
}

That is all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and do not forget to share!

7. Summary

In this section, you learned,

  • Spring boot and Hashicorp vault introduction
  • Steps to setup Hashicorp vault on docker
  • Steps to set up the secret in the Hashicorp vault
  • Steps to implement Hashicorp vault in Spring Boot and interact with the database

You can download the sample application as an Eclipse project in the Downloads section.

8. Download the Project

This was an example of implementing Hashicorp Vault in a Spring Boot application.

Download
You can download the full source code of this example here: An Intro to Spring Cloud Vault

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button