DatabaseUserDetailsService.java
package com.taxonomy.security.service;
import com.taxonomy.security.model.AppUser;
import com.taxonomy.security.repository.UserRepository;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;
/**
* Loads user details from the database for Spring Security authentication.
* <p>
* Only active in form-login mode (without Keycloak). In the Keycloak profile,
* authentication is handled by the OIDC provider — no local UserDetailsService is needed.
*/
@Service
@Profile("!keycloak")
public class DatabaseUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public DatabaseUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AppUser user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return User.builder()
.username(user.getUsername())
.password(user.getPasswordHash())
.disabled(!user.isEnabled())
.authorities(
user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toSet())
)
.build();
}
}