Skip to content
Albert edited this page Jun 17, 2024 · 41 revisions

Project Overview

  • Create a Spring Boot Security Thymeleaf project
  • Build a .jar
  • Deploy .jar by SSH to EC2 AWS

Tech stack

References

Spring Boot Security:

AWS EC2 & SSH:

Thymeleaf:

Tree-folder

image

Step-by-step guide

Spring Boot Deploy:

  1. Create and test Spring Boot App
  2. Get app–0.0.1-SNAPSHOT.jar from target: app.jar
  3. Create AWS EC2 VM T2.micro Linux
  4. Login from local to EC2 VM with SSH .pem file
  5. Install JAVA and upload app.jar
  6. Run app.jar

Create the SNAPSHOT

Ensure Maven is Installed. Make sure you have Maven installed on your system. You can check this by running:

sh mvn -version

Navigate to the Project Directory open a terminal or command prompt and navigate to the root directory of your Spring Boot project.

Build the Project. To build the project and package it into a JAR (Java Archive) file, use the following Maven command:

mvn clean package

image

AWS EC2

Create Virtual Machine

For instance, you could create a Linux Amazon machine or a Ubuntu Server 22.0, by creating the VM you should not forget the key pair to connect to the instance from your local.

image

image

image

image

Connect VM Ubuntu Linux

Connect to instance: SSH client

image

image

image

Note

Please keep the keypair handy in the same folder as a jar file: demosecurity.jar & securitydeploy.pem

Now, go to the folder where your downloaded .pem file is and open the terminal to connect to the EC2 instance via SSH. Get the SSH command from the EC2 AWS visual console following these steps:

Instance ID (securitydeploy): i-014b57b86a116762a

  1. Open an SSH client.
  2. Locate your private key file. The key used to launch this instance is securitydeploy.pem
  3. Run this command, if necessary, to ensure your key is not publicly viewable.

chmod 400 "securitydeploy.pem"

  1. Connect to your instance using its Public DNS:

ec2-3-68-82-57.eu-central-1.compute.amazonaws.com

image

After that, copy the SSH command like it is shown in the SSH tab on Connect to Instance. paste it in the terminal and click enter.

ssh -i "securitydeploy.pem" ubuntu@ec2-3-68-82-57.eu-central-1.compute.amazonaws.com

image

Connect to instance: EC2 instance Connect

image

image

Install Java

image

upload & run .jar

You could:

  1. use the ssh connection or
  2. (if it fails) excute upload with scp right from your local directory:

image

scp: security copy

scp -i securitydeploy.pem demosecurity.jar ubuntu@ec2-3-68-82-57.eu-central-1.compute.amazonaws.com:~/

Now, we need ssh to access Ubuntu 22 server on EC2 to run our .jar:

image

And run it:

image

java -jar demosecurity.jar

Port 8088

Our Spring Boot is configured at port 8088, so we nee to edit the inbound rules:

image

Code

Java Spring

MvcConfig

image

The class MvcConfig that implements WebMvcConfigurer. The addViewControllers method configures simple automated controllers for specific URLs. It maps:

  • the URL "/home" to the "home" view,
  • the root URL "/" to the "home" view,
  • the URL "/hello" to the "hello" view,
  • and the URL "/login" to the "login" view.

This means when users visit these URLs, they are directed to the corresponding HTML pages without needing a dedicated controller for each view.

MvcConfig v0.3.0
package com.example.demoSecurity;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/home").setViewName("home");
		registry.addViewController("/").setViewName("home");
		registry.addViewController("/hello").setViewName("hello");
		registry.addViewController("/login").setViewName("login");
	}

}

WebSecurityConfig

image

This code configures web security for an application.

The WebSecurityConfig class is annotated with @Configuration and @EnableWebSecurity, indicating that it is a configuration class for web security.

configure

The configure method sets up HTTP security.

  • It allows unrestricted access to the root URL ("/") and the home URL ("/home")
  • While requiring authentication for any other requests
  • Form-based login is enabled with the login page set to "/login": login and logout functionalities are allowed for all users.

userDetailsService

The userDetailsService method creates an in-memory user store.

  • It defines a single user with the username "albert", password "1234", and role "USER".
  • The User.withDefaultPasswordEncoder() method is used to create the user, which is then managed by an InMemoryUserDetailsManager.

This setup is mainly for development and testing purposes and not recommended for production due to the use of plain text passwords and in-memory storage.

WebSecurityConfig
package com.example.demoSecurity;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.antMatchers("/", "/home").permitAll()
				.anyRequest().authenticated()
				.and()
			.formLogin()
				.loginPage("/login")
				.permitAll()
				.and()
			.logout()
				.permitAll();
	}

	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		UserDetails user =
			 User.withDefaultPasswordEncoder()
				.username("albert")
				.password("1234")
				.roles("USER")
				.build();

		return new InMemoryUserDetailsManager(user);
	}
}
userDetailsService() with several users
@Bean
@Override
public UserDetailsService userDetailsService() {
    UserDetails user1 = User.withDefaultPasswordEncoder()
            .username("albert")
            .password("1234")
            .roles("USER")
            .build();

    UserDetails user2 = User.withDefaultPasswordEncoder()
            .username("brenda")
            .password("5678")
            .roles("USER")
            .build();

    UserDetails admin = User.withDefaultPasswordEncoder()
            .username("charlie")
            .password("adminpass")
            .roles("ADMIN")
            .build();

    return new InMemoryUserDetailsManager(user1, user2, admin);
}

HTML

Hello

This HTML file generates a "Hello" page personalized for the logged-in user. It uses Thymeleaf and Spring Security integration for rendering dynamic content and handling security.

  • Thymeleaf: A server-side Java template engine.
  • th:inline="text": Enables text inline mode for Thymeleaf.
  • [[${#httpServletRequest.remoteUser}]]: Displays the username of the logged-in user.
  • Logout Form: A form to sign out the user.
Hello.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
    </body>
</html>

Home

This HTML file creates a home page with a welcome message and a link to the "Hello" page.

  • Welcome Message: A simple "Welcome!" heading.
  • Link to Hello Page: A link that navigates to the "Hello" page.
Home.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>

        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>

Login

This HTML file provides a login form for users to sign in, with messages for invalid login attempts and successful logout.

  • Error Message: Displays if login fails.
  • Logout Message: Displays if the user has logged out.
  • Login Form: Contains fields for username and password, and a submit button. The form action is set to /login.
Login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

Set environment variables

  1. Install direnv:
sudo apt update
sudo apt install direnv
  1. Configure direnv:
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
source ~/.bashrc
  1. Create the .envrc file:
cd ~/my-spring-boot-app
echo 'export DB_USERNAME="ReadOnlyUser"' > .envrc
echo 'export DB_PASSWORD="P@ssw0rd1"' >> .envrc
echo 'export API_KEY="sk-proj-oqHcx6hjqPNhRZi7MpfHT"' > .envrc

  1. Allow direnv to load the .envrc file:
direnv allow
  1. Verify the Environment Variable
echo $API_KEY
sk-proj-oqHcx6hjqPNhRZi7MpfHT
  1. Configure Spring Boot to Use Environment Variables:
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}

Output

Local

Open the browser at http://localhost:8088/

image

image

image

image

Remote (AWS EC2 t2.micro Ubuntu 22.0 server)

Spring Boot Security executing at AWS EC2 Ubuntu 22.0 server. It publishes at URL:

http://ec2-3-68-82-57.eu-central-1.compute.amazonaws.com:8088/home: link

http://ec2-3-79-242-20.eu-central-1.compute.amazonaws.com:8088/home: link

Note

When an AWS EC2 instance, such as a t2.micro, is stopped and then started again, it receives a new public IP address. This occurs because public IP addresses are dynamically assigned from a pool of available IPs at the time the instance starts. Unlike Elastic IPs, which are static and persist across instance stops and starts, standard public IPs are released when the instance stops and a new one is assigned upon start. To maintain a consistent IP address, an Elastic IP must be associated with the instance.

image

image

image