Spring Security + Thymeleaf — In memory User Details Configuration (Spring Security — 1)

Amila Iroshan
The Fresh Writes
Published in
7 min readFeb 7, 2023

Security is a software non functional requirement and usually designing the software application we should consider the security things.
Now a days most of the times even the SRS(software requirement specification) does not specify how to handle security in our application.
It means security is a basic and mandatory key fact of the system and as a good software engineer we should securing our application.

Introduction

Spring security is a framework and introduce by spring team for secure java based applications. Basically it provides authentication, authorization and access control to java applications. Besides that it provides a flexible architecture that allows developers to easily customize and extend the security features to meet their specific needs.

Pre requisite,

Basic knowledge of java and spring boot framework.

Overview

This sample application express how to handle authentication and authorization using spring security. In addition to that after successful authentication user redirect based on user roles with custom success handler.

What is:

Authentication:
It is about knowing the identification of the user, who is using the application. If the user’s identification is verified by the system user permit to login to system. Other wise user unable to login to application. The authentication is based on user credentials (UserName and Password).

Autherization
The authorization is the giving authority to the user to use the specific component of the application. Usually we are giving role based authorization to users. Roles are the authorization levels.
Eg: Typical application contains several components and any users can access the some components. Besides that some components can access only granted user roles.

Principle : Refers to the currently authenticated user.
Granted authority: Refers to the permission of the authenticated user.
Role : Refers to a group of permissions of the authenticated user.

Lets begin,

I’ll explain how the spring security works on our application using below diagram.

Spring Security Work Flow

When we send the http request to simple spring boot web application, the http request first reach out to dispatcher servlet. The dispatcher servlet behave as a front door to our spring based web application. But after adding spring security dependency to system the authentication filter come into picture and it works as a front controller.

Filter chain

As illustrate on above Spring Security work flow diagram first http request reach out to the authentication filter. There are several registered authentication filters available and with enable of “debug=true” you can see registered authentication filters.

Available Security Filters

When the request is intercepted by the appropriate AuthenticationFilter it retrieves the username and password from the request and creates the Authentication Object. AuthenticationManager refer the authentication Object created the filter will then call the authenticate method of the Authentication Manager.

The AuthenticationProvider is an interface and It has various implementations like CasAuthenticationProvider, DaoAuthenticationProvider. Depending on the implementation an appropriate AuthenticationProvider implementation is used.

UserDetails service- The AuthenticationProvider fetches the User Object corresponding to the username. It fetches this User Object from either a database, internal memory or other sources.
In my example it uses inmemory user details service. This User object credentials are then compared with the incoming Authentication Object credentials. If Authentication is successful then the Principal Authentication Object is returned in response.

1. Spring In memory authentication uses InMemoryUserDetailsManager internally store and retreive the user-related information required for authentication.
2. We will see how to create and store the user to the in memory using InMemoryUserDetailsManager
3. So whenever the user requests for any details,the request is filtered and passed to AuthenticationManager,which tries to authenticate thr request by using the UserDetailsService.
4. The UserDetgailsService is responsible for retreiving the correct user details,InMemoryUserDetailsManager indirectly implements UserDetailsService interface.The InMemoryUserDetailsManager reads the in-memory hash-map and loads the UserDetails by calling the loadUserByUserName() method.
5. Once the UserDetails is loaded via InMemoryUserDetailsManager and the authentication is successful,the SecurityContext will be updated and the request will proceed to the DispatcherServelet.

Security context is the place where store the jsession id and it checks across the jsession id in cookie of incoming request.

Jsession Id

Lets start the implementation

Create spring boot application.
— Navigate to https://start.spring.io.

— Choose either Gradle or Maven as build tool. In here I’m using mavean,Java 18 and .jar as packaging.

  • Click Dependencies and select Spring starter Web, Spring security and Thymeleaf. Here is my 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 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.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.security</groupId>
<artifactId>security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>


</plugins>
</build>

</project>

Then create class called SecurityConfig and extends WebSecurityConfigurerAdapter. After that override configure(HttpSecurity http) method.In this method we can specify that how to give the permission to out pages using role based. In my application I’m giving that no authentication to about page, css and images folder. Besides that specify login page and success handler. The success handler is place where we write out custom business logics after success authentication.The configureGlobal method sets up an in-memory authentication with the user "user" ,password "userpass" and role “USER”.

@Configuration
@EnableWebSecurity(debug=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{

private LoginSuccessHandler authenticationSuccessHandler;

@Autowired
public SecurityConfig(LoginSuccessHandler authenticationSuccessHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
}

@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/about").permitAll()
.antMatchers( "/css/**", "/images/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(authenticationSuccessHandler)
.permitAll()
.and()
.logout()
.permitAll()
.and().csrf().disable();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("userpass").roles("USER")
.and()
.withUser("admin").password("adminpass").roles("ADMIN");
}

}
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN")) {
httpServletResponse.sendRedirect("/admin");
} else {
httpServletResponse.sendRedirect("/user");
}
}
}

Here is code snippet of my WebController.

@Controller
public class WebController {
@GetMapping(value = {"/","/login"})
public String getLogin() {
return "login";
}
@GetMapping("/admin")
public String getAdminPanel() {
return "admin";
}
@GetMapping("/user")
public String getUser() {
return "user";
}
@GetMapping("/home")
public String getHome() {
return "home";
}
@GetMapping("/about")
public String getAbout() {
return "about";
}
}

You can find the my view pages with thymeleaf templaes.

login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Login - Spring Security Example</title>
<link rel="stylesheet" href="main.css">
<style type="text/css">
body div {
text-align: center;
}
label, input[type=text], input[type=password] {
display: inline-block;
width: 150px;
margin: 5px;
}
input[type=submit] {
width: 60px;
margin: 10px;
padding: 10px;
text-align: center;
}
</style>
</head>
<body>
<div>
<div>
<h2>Spring Security Login Form</h2>
</div>
<div th:if="${param.error}">
<h3>Invalid username and password.</h3>
</div>
<div th:if="${param.logout}">
<h3>You have been logged out.</h3>
</div>
<div>
<form th:action="@{/login}" method="post">
<div><label>Username: </label> <input type="text" name="username" /></div>
<div><label>Password: </label><input type="password" name="password" /></div>
<div><input type="submit" value="Login" /></div>
</form>
</div>
</div>
</body>
</html>

home.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Home</title>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<div sec:authorize="hasRole('ADMIN')">
This content is only shown to admins.
</div>
<div sec:authorize="hasRole('USER')">
This content is only shown to users.
</div>
<br/>
<div>
User: <span sec:authentication="name">NOT FOUND</span>
Spring Roles: <span sec:authentication="principal.authorities">NOT FOUND</span>
</div>
<br/>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>

admin.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>Welcome,This is admin page</h1>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<button><a th:href="@{/home}">Go To Home</a></button>
<input type="submit" value="Sign Out"/>
</form>
</div>
</div>
</body>
</html>

user.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>Welcome,This is user page</h1>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<button><a th:href="@{/home}">Go To Home</a></button>
<input type="submit" value="Sign Out"/>
</form>
</div>
</div>
</body>
</html>

Let’s run our application. Once the application is up and running, login to system by visiting http://localhost:8080/.

Here are some screen shots of running system.

Login

login

Home page

home page

About page

About page

You can find the complete code for this example on GitHub

Happy Coding !!

--

--

Amila Iroshan
The Fresh Writes

Software Engineer | Open Source Contributor | Tech Enthusiast