Spring Security – An Introduction

Security is an essential non-functional aspect of an application. A secure application usually authenticates the users and may also check for authorization to perform tasks based on the user role.
Authentication and authorization are the main components that decide if a user can access and perform the task on the application.

Spring security framework provides complete support for authentication and authorization functionalities of applications.

In this post, we will learn the basics of Spring security, its main components, and create a simple application that implements these features.

Spring Security – Hello World example

The first program that we usually write while learning any language or framework is “Hello world!”. Let us follow the tradition and create our “Hello World” application. 😛

Create a Spring boot application with the below dependencies.

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

The spring-boot-starter-security is the starter Spring boot dependency that provides all the required Spring security related libraries.

The spring-boot-starter-web dependency provides the necessary dependencies to build a web application.

Create a simple Rest controller, as shown below. The Rest controller returns the message “Hello World!!”.

@RestController
public class HelloController {
	
	@GetMapping("/")
	public String getMessage() {
		return "Hello World!!";
	} 
}

Run the Spring boot application.

Spring security generates a random UUID string that is the password of the application.

spring security basic example

If we access the application, we get the default login page as shown below.

Enter the default username user and the password, the random UUID string printed on the application’s console.

Spring security default login page

After the successful authentication, our controller displays the “Hello World!!” text, as shown below.

spring security hello world example

How spring security works?

In the previous section, we saw the traditional “Hello World” example of Spring security. Now, let us learn how the Spring security framework internally works to handle application security.

The below image shows the main components of Spring security.

spring security architecture
  • Authentication filter: The authentication filter receives user requests and forwards them to the authentication manager for authentication.
  • Authentication Manager: The authentication manager uses the authentication provider to perform the authentication process.
  • Authentication Provider: The authentication provider validates if the user exists in the system using the User details service and also validates the password using the password encoder.
  • User details service: Implements user management responsibility. It searches for the existing user, which is used in the authentication of the user by the provider.
  • Password Encoder: Encodes the password before saving and matches the encoded password against a encode type.
  • Security Context: The security context holds the user details after the successful authentication and authorization process.

Spring security is a highly customizable framework. We can customize any part of the framework configuration, such as authentication logic, custom representation of the user details, etc.

Customizing the spring security

Customization of spring security is simple and similar to other spring frameworks.

We define custom configurations or override the default implementation to customize certain parts of the framework according to our application requirements.

Changing the default username and password

We can override the username and password by adding the following properties into the Spring boot application’s application.properties configuration file under the /src/main/resources/ directory.

spring.security.user.name=arun
spring.security.user.password=test

Creating a new user

In this section, let us see how to use InMemoryUserDetailsManager implementation to create a new user.

@Configuration
public class CustomSecurityConfig {
	
	@Bean
	public UserDetailsService userDetailsService() {
		var userDetailsService = new InMemoryUserDetailsManager();
		var user = User.withUsername("arun").password("12345").authorities("read").build();
		userDetailsService.createUser(user);
		return userDetailsService;
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}
}

We have created a new user instance with username arun, password 12345, and a role called read. We used InMemoryUserDetailsManager implementation(which implements UserDetailsService interface) instance to create a new user instance.

InMemoryUserDetailsManager implementation is a basic implementation provided by the Spring security and is not practical to use in the production environment.

We also need to define a PasswordEncoder bean if we define a custom User details service.

We have defined a NoOpPasswordEncoder bean, that applies no encoding(Not suitable for Production environment) to the user password.

Overriding the security configuration

To override the security configuration, we can create a custom security configuration class and override the required configure() method of the class WebSecurityConfigurerAdapter.

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http.formLogin().defaultSuccessUrl("/home", true);
		http.authorizeRequests().anyRequest().authenticated();
	}
}

We have set the default success URL of the application to “/home” in the above example.

The above configuration applies application security to all the incoming requests.

If we restart the application, we get redirected to the /home URL path after the successful authentication process, and the default error screen will be displayed(as there is no handler available for this URL path).

spring security example

Customizing the authentication logic

To customize authentication logic, we can follow the below steps:

  • Create a custom class and implement the AuthenticationProvider interface.
  • Implement the authenicate() and supports() method.
  • Inject the custom provider into the security configuration class and configure the application to make use of the injected custom provider instance with the help of the configure() method.

Let us start with creating a custom authentication provider class called CustomAuthenticationProvider.

Mark the class with @Component annotation so that it will make the class injectable to the security configuration class.

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		String username = authentication.getName();
		String password = String.valueOf(authentication.getCredentials());
		if ("arun".equals(username) && "12345".equals(password)) {
			return new UsernamePasswordAuthenticationToken(username, password, Arrays.asList());
		} else {
			throw new BadCredentialsException("authentication Failed!!");
		}
	}
	@Override
	public boolean supports(Class<?> authentication) {
		return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
	}
}

We need to override the authenticate() method, which handles all the logic related to the authentication.

If the authentication is successful, we return an implementation of the Authentication object that contains all the authentication details.

Spring security registers these authentication details into Security Context.

In the above example, we have returned the authentication instance of type UsernamePasswordAuthenticationToken, which represents the logged-in user’s authentication object.

The supports() method checks for the supported authentication type.

With the above example, we are checking if the Authentication object has a username and password, and it represents the UsernamePasswordAuthenticationToken authentication implementation class.

If the credentials are not correct, the application throws BadCredentialsException with a message.

bad credentials spring security

Configure the security configuration class by injecting the authentication provider and configuring the authentication provider.

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private CustomAuthenticationProvider authProvider;
	@Override
	public void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.authenticationProvider(authProvider);
	}
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http.formLogin().defaultSuccessUrl("/home", true);
		http.authorizeRequests().anyRequest().authenticated();
	}
	@Bean
	public UserDetailsService userDetailsService() {
		var userDetailsService = new InMemoryUserDetailsManager();
		var user = User.withUsername("arun").password("12345").authorities("read").build();
		userDetailsService.createUser(user);
		return userDetailsService;
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}
}

With the help of AuthenticationManagerBuilder, we have configured our application to use the custom authentication provider implementation.

Congratulations! We have learned the basics of Spring security and also learned how to customize a few of the security configurations of our Spring boot application.

Conclusion

In this article, we learned the basics of Spring security, how internally Spring security handles the authentication, and how to customize a few of the Spring security configurations.

Example code is available on Github.

Leave a Reply