Basics Of Spring Security User Management

User management in an application defines how a user is created/updated, characteristics of a user(credentials, email address, a flag to indicate account status), and the authentication process.

In this article, we will learn how spring security handles user management. We will also learn the main components of Spring security user management.

We will create a small example application and customize a few of the components of the user management.

Components of user management

The below image shows the main components of Spring security user management.

Spring security user management

The main components and their role are:

  • UserDetailsManager: This interface extends UserDetailsService interface. It is responsible for user create, updates, or delete operations.
  • UserDetailsService: This interface defines a method that finds the user by username.
  • UserDetails: This interface describes the user.
  • GrantedAuthority: A user can have one or more authorities that are represented by the GrantedAuthority interface.

Customizing the user details

In this example, we will learn how to customize the default user representation provided by the spring security.

We will create a Spring boot application with spring security dependency. We define a custom User class to represent the user and also create a custom user details service to load the user from an in-memory list.

Create an application

Create a Spring boot application with the following 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>

We have added Web and security starter Spring boot dependencies to our applications pom.xml file.

Define a User details class

Create a custom User.java class that represents our application’s user.

Our custom user class needs to implement Spring security’s UserDetails interface.

The below code shows a simple implementation of the UserDetails contract.

public class User implements UserDetails {
	private static final long serialVersionUID = 1L;
	private final String username;
	private final String password;
	private final String authority;
	public User(String username, String password, String authority) {
		this.username = username;
		this.password = password;
		this.authority = authority;
	}
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return List.of(() -> authority);
	}
	@Override
	public String getPassword() {
		return this.password;
	}
	@Override
	public String getUsername() {
		return this.username;
	}
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}
	@Override
	public boolean isEnabled() {
		return true;
	}
}

The Spring securities UserDetails interface has the following methods:

  • getAuthorities(): This method returns all the authorities a user has. The GrantedAuthority contract represents user authority.
  • These authorities describe the privileges a user has(like read, write, update, etc.) or actions a user can perform. In our example, we have a simple String field called authority that represents the user’s authority.
  • In our example, we have returned true for the below methods, as it is a simple implementation. We can also add the required logic to customize the behavior.
  • getPassword() & getUsername(): Returns the user credentials(password & username).
  • isAccountNonExpired(): Return true if user account is not expired.
  • isAccountNonLocked(): Return true if user account is not locked.
  • isCredentialsNonExpired(): Return true if user credentials are not expired.
  • isEnabled(): Return true if user is enabled.

Implement a custom UserDetailsService

To use the newly created custom user details class, we need to implement the UserDetailsService interface.

The UserDetailsService is responsible for loading the user by username from a database/LDAP or any other storage(From a local collection in our example).

Create a custom InMemoryUserDetailsService class that implements UserDetailsService interface.

public class InMemoryUserDetailsService implements UserDetailsService {
	private final List<UserDetails> users;
	public InMemoryUserDetailsService(List<UserDetails> users) {
		this.users = users;
	}
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		return users.stream()
				.filter(u -> u.getUsername().equals(username))
				.findFirst()
				.orElseThrow(() -> new UsernameNotFoundException("Username not found!!"));
	}
}

We are maintaining users in a local collection called users.

The loadUserByUsername method searches for available user instances from the “users” collection and returns the user instance if the username matches.

If the username doesn’t match any of the existing users, it throws UsernameNotFoundException.

Define the security configuration

To wire the custom UserDetails and UserDetailsService implementation to spring security, we need to configure the UserDetailsService bean.

Create a spring configuration AuthConfig.java class, as shown below.

@Configuration
public class AuthConfig {
	
	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails u = new User("arun", "12345", "READ");
		List<UserDetails> users = List.of(u);
		return new InMemoryUserDetailsService(users);
	}
	@Bean
	public PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}
}

We have created an instance of the User class, which is our customized UserDetails instance.

The user will have a username arun, password 12345, and an authority READ.

We have also created a list of users and passing it to the class InMemoryUserDetailsService‘s constructor that implementations UserDetailsSerice interface.

The collection of users passed to the constructor adds the users into the locally defined in-memory users list of InMemoryUserDetailsService class.

We also have to define a Password encoder bean if we are customizing the UserDetailsService.


Create a simple HTTP GET endpoint, as shown below.

The user will land on this path after successful authentication.

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

Testing the implementation

Start the Spring boot application. Access the application from a browser(at location http://localhost:8080).

Spring security displays the default login page, as shown below.

Enter the credentials of the user and click on the Sign in button.

custom user details example spring boot

We get a hello message if the user gets authenticated successfully.

custom user details example spring security

Conclusion

In this article, we learned how spring security represents the user details, user authorities, etc.

We also learned how spring security provides basic functionalities required to load, manage users, and their authentication.

We have also customized user details by creating a simple user implementation and custom user details service to use the custom user implementation.

Example code is available on GitHub.

You may also be interested in

Leave a Reply