Loading Users

In our previous lesson, we added Spring Security to protect our blog platform's endpoints.

Now we'll implement user loading functionality to enable authentication based on our existing User entity.

This implementation will bridge the gap between Spring Security's user management and our domain model.

Creating the UserDetails Class

The BlogUserDetails class adapts our domain User entity to Spring Security's authentication system:

package com.devtiro.blog.security; import com.devtiro.blog.domain.entities.User; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; import java.util.UUID; @Getter @RequiredArgsConstructor public class BlogUserDetails implements UserDetails { private final User user; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(new SimpleGrantedAuthority("ROLE_USER")); } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getEmail(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } public UUID getId() { return user.getId(); } }

Extending the User Repository

The UserRepository needs a method to find users by their email addresses as that's what the user will use to log in:

@Repository public interface UserRepository extends JpaRepository<User, UUID> { Optional<User> findByEmail(String email); }

Implementing the UserDetails Service

The BlogUserDetailsService loads user data and converts it to Spring Security's UserDetails format:

package com.devtiro.blog.services.impl; import com.devtiro.blog.domain.BlogUserDetails; import com.devtiro.blog.domain.entities.User; import com.devtiro.blog.repositories.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @RequiredArgsConstructor public class BlogUserDetailsService implements UserDetailsService { private final UserRepository userRepository; @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + email)); return new BlogUserDetails(user); } }

Handling Authentication Errors

Let's add handling for authentication failures to our ErrorController:

@ExceptionHandler(BadCredentialsException.class) public ResponseEntity<ApiErrorResponse> handleBadCredentialsException(BadCredentialsException ex) { ApiErrorResponse error = ApiErrorResponse.builder() .status(HttpStatus.UNAUTHORIZED.value()) .message("Incorrect username or password") .build(); return new ResponseEntity<>(error, HttpStatus.UNAUTHORIZED); }

Summary

  • Created BlogUserDetails to adapt our domain user model to Spring Security
  • Extended UserRepository with email-based user lookup
  • Implemented BlogUserDetailsService to load users during authentication
  • Added error handling for authentication failures
© 2026 Devtiro Ltd. All rights reserved