CustomUserDetailsService.java
package com.ctrlbuy.webshop.service;
import com.ctrlbuy.webshop.security.entity.User;
import com.ctrlbuy.webshop.security.repository.UserRepository;
// ===== JPA IMPORTS =====
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
// ===== LOGGING IMPORTS =====
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* đĄïž RAILWAY COMPATIBLE CustomUserDetailsService
*
* FIXED VERSION som anvÀnder EntityManager istÀllet för Spring Data JPA
* repository methods för att undvika "u1_0.createdAt" query generation problem.
*/
@Service
@Primary
public class CustomUserDetailsService implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
private final UserRepository userRepository;
@PersistenceContext
private EntityManager entityManager;
@Autowired
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
/**
* đ RAILWAY SAFE loadUserByUsername
*
* AnvÀnder EntityManager med explicit JPQL queries istÀllet för
* Spring Data JPA method names som genererar problematiska column aliases.
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("đ Spring Security försöker autentisera: {}", username);
try {
// đĄïž SĂKER METOD 1: Först försök hitta med username
User user = findUserByUsernameOrEmailSafe(username);
if (user != null) {
logger.info("â
AnvÀndare hittades: {} (ID: {})", user.getUsername(), user.getId());
logger.info("đ AnvĂ€ndare aktiv: {}, Email verifierad: {}", user.isActive(), user.isEmailVerified());
// Logga roller för debugging
if (user.getRole() != null) {
logger.info("đ AnvĂ€ndarens single role: {}", user.getRole());
}
if (user.getRoles() != null && !user.getRoles().isEmpty()) {
logger.info("đ AnvĂ€ndarens roles lista: {}", user.getRoles());
}
return createUserDetails(user);
}
// Om ingen anvÀndare hittades
logger.warn("â ïž AnvĂ€ndare hittades inte: {}", username);
throw new UsernameNotFoundException("User not found: " + username);
} catch (UsernameNotFoundException e) {
// Re-throw UsernameNotFoundException
throw e;
} catch (Exception e) {
logger.error("â Fel vid autentisering av '{}': {}", username, e.getMessage(), e);
throw new UsernameNotFoundException("Autentiseringsfel för: " + username, e);
}
}
/**
* đĄïž SAFE findUserByUsernameOrEmailSafe
*
* AnvÀnder EntityManager för att undvika Spring Data JPA query generation.
* Söker först med username, sedan med email om username inte hittas.
*/
private User findUserByUsernameOrEmailSafe(String usernameOrEmail) {
try {
logger.info("đ Söker anvĂ€ndare med username/email: {}", usernameOrEmail);
// STEG 1: Försök hitta med username
Query usernameQuery = entityManager.createQuery(
"SELECT u FROM User u WHERE u.username = :identifier", User.class);
usernameQuery.setParameter("identifier", usernameOrEmail);
List<User> usernameResults = usernameQuery.getResultList();
if (!usernameResults.isEmpty()) {
User user = usernameResults.get(0);
logger.info("â
AnvÀndare hittades med username: {}", user.getUsername());
return user;
}
logger.info("đ Username '{}' hittades inte, försöker med email...", usernameOrEmail);
// STEG 2: Försök hitta med email
Query emailQuery = entityManager.createQuery(
"SELECT u FROM User u WHERE u.email = :identifier", User.class);
emailQuery.setParameter("identifier", usernameOrEmail);
List<User> emailResults = emailQuery.getResultList();
if (!emailResults.isEmpty()) {
User user = emailResults.get(0);
logger.info("â
AnvÀndare hittades med email: {} (username: {})",
user.getEmail(), user.getUsername());
return user;
}
logger.warn("â ïž Ingen anvĂ€ndare hittades med identifier: {}", usernameOrEmail);
return null;
} catch (Exception e) {
logger.error("â Fel vid sökning av anvĂ€ndare '{}': {}", usernameOrEmail, e.getMessage());
return null;
}
}
/**
* đ Enhanced createUserDetails med förbĂ€ttrad rollhantering
*/
private UserDetails createUserDetails(User user) {
logger.info("đš Skapar UserDetails för anvĂ€ndare: {}", user.getUsername());
// â
FĂRBĂTTRAD: Skapa authorities manuellt med rĂ€tt ROLE_ prefix
List<GrantedAuthority> authorities = new ArrayList<>();
boolean isAdmin = false;
// ===== ROLLHANTERING =====
// METOD 1: Kolla single role field (om du har det)
if (user.getRole() != null) {
String role = user.getRole().toString(); // ADMIN eller USER
String roleWithPrefix = role.startsWith("ROLE_") ? role : "ROLE_" + role;
authorities.add(new SimpleGrantedAuthority(roleWithPrefix));
logger.info("đ Single role tillagd: {}", roleWithPrefix);
if ("ADMIN".equals(role) || "ROLE_ADMIN".equals(roleWithPrefix)) {
isAdmin = true;
}
}
// METOD 2: Kolla roles list (String List)
if (user.getRoles() != null && !user.getRoles().isEmpty()) {
for (String role : user.getRoles()) {
String roleWithPrefix = role.startsWith("ROLE_") ? role : "ROLE_" + role;
// Undvik duplicates
boolean alreadyExists = authorities.stream()
.anyMatch(auth -> auth.getAuthority().equals(roleWithPrefix));
if (!alreadyExists) {
authorities.add(new SimpleGrantedAuthority(roleWithPrefix));
logger.info("đ Role frĂ„n lista tillagd: {}", roleWithPrefix);
}
if ("ADMIN".equals(role) || "ROLE_ADMIN".equals(roleWithPrefix)) {
isAdmin = true;
}
}
}
// METOD 3: Special case för admin username (backup)
if ("admin".equals(user.getUsername()) ||
"superadmin".equals(user.getUsername()) ||
"fredrik".equals(user.getUsername())) {
isAdmin = true;
logger.info("đ Admin anvĂ€ndare identifierad via username: {}", user.getUsername());
// Se till att admin har ROLE_ADMIN
boolean hasAdminRole = authorities.stream()
.anyMatch(auth -> "ROLE_ADMIN".equals(auth.getAuthority()));
if (!hasAdminRole) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
logger.info("đ ROLE_ADMIN tillagd för admin anvĂ€ndare");
}
}
// FALLBACK: Alla anvÀndare ska ha Ätminstone ROLE_USER
if (authorities.isEmpty()) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
logger.info("đ Fallback: ROLE_USER tillagd (inga andra roller hittades)");
} else {
// Se till att alla har ROLE_USER (om de inte bara har admin)
boolean hasUserRole = authorities.stream()
.anyMatch(auth -> "ROLE_USER".equals(auth.getAuthority()));
if (!hasUserRole) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
logger.info("đ ROLE_USER tillagd som base role");
}
}
logger.info("đ€ AnvĂ€ndare Ă€r admin: {}", isAdmin);
logger.info("đ Slutgiltiga authorities: {}", authorities);
// ===== SKAPA SPRING SECURITY USERDETAILS =====
// â
FIXAT: AnvÀnd isEnabled() istÀllet för isActive() för Railway compatibility
boolean isAccountEnabled = user.isEnabled(); // DETTA ĂR KEY FIXEN!
logger.info("đ Account enabled: {}", isAccountEnabled);
logger.info("đ§ Email verified: {}", user.isEmailVerified());
UserDetails userDetails = org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(authorities)
.accountExpired(false)
.accountLocked(false)
.credentialsExpired(false)
.disabled(!isAccountEnabled) // â
ANVĂND isEnabled() som matchar User entity
.build();
logger.info("â
UserDetails skapat för anvÀndare: {}", user.getUsername());
return userDetails;
}
/**
* đ SAFE validateUserExists - för extra validering
*/
public boolean validateUserExists(String usernameOrEmail) {
try {
User user = findUserByUsernameOrEmailSafe(usernameOrEmail);
return user != null;
} catch (Exception e) {
logger.error("â Fel vid validering av anvĂ€ndare '{}': {}", usernameOrEmail, e.getMessage());
return false;
}
}
/**
* đ SAFE getUserByUsername - för andra services
*/
public User getUserByUsername(String username) {
try {
return findUserByUsernameOrEmailSafe(username);
} catch (Exception e) {
logger.error("â Fel vid hĂ€mtning av anvĂ€ndare '{}': {}", username, e.getMessage());
return null;
}
}
/**
* đ SAFE isUserActive - för status kontroll
*/
public boolean isUserActive(String username) {
try {
User user = findUserByUsernameOrEmailSafe(username);
if (user == null) {
return false;
}
boolean isActive = user.isEnabled(); // â
ANVĂND isEnabled()
logger.info("đ AnvĂ€ndare '{}' Ă€r aktiv: {}", username, isActive);
return isActive;
} catch (Exception e) {
logger.error("â Fel vid aktivitetskontroll för '{}': {}", username, e.getMessage());
return false;
}
}
}