Spring Security的User类能否直接搭配CrudRepository使用?求替代继承的方案
User and Spring Data JPA Hey there! Let's tackle this frustrating error you're running into. The root cause is straightforward: Spring Security's org.springframework.security.core.userdetails.User class isn't a JPA managed entity—it lacks the @Entity annotation, so Spring Data JPA can't recognize it as a type to persist or query against. And just to clarify: the User class is actually final, so you can't even inherit from it (that "inheritance solution" you found might have been referring to implementing the UserDetails interface instead, which is a different approach).
Here are two cleaner, more maintainable solutions that don't force you to hack around the User class:
Option 1: Separate JPA Entity + Security User Conversion (Recommended for Flexibility)
This approach keeps your database model and security model decoupled, making it easier to extend either side later on.
- Create a custom JPA entity to map your Derby database table:
@Entity @Table(name = "app_users") // Avoid "users" as it may be a reserved word in some databases public class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; private boolean enabled; // Add extra fields like email, full name as needed // Getters, setters, and constructors here }
- Create a Spring Data Repository for your entity:
public interface AppUserRepository extends CrudRepository<AppUser, Long> { Optional<AppUser> findByUsername(String username); }
- Implement a custom
UserDetailsServiceto convert yourAppUserentity into Spring Security'sUserwhen needed:
@Service public class AppUserDetailsService implements UserDetailsService { private final AppUserRepository appUserRepository; public AppUserDetailsService(AppUserRepository appUserRepository) { this.appUserRepository = appUserRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { AppUser appUser = appUserRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); // Build the Spring Security User object from your entity return User.withUsername(appUser.getUsername()) .password(appUser.getPassword()) .enabled(appUser.isEnabled()) .authorities("ROLE_USER") // Replace with actual permissions from your DB if needed .build(); } }
Option 2: Implement UserDetails Directly in Your JPA Entity
If you prefer a more streamlined approach (no conversion layer), you can make your custom entity implement Spring Security's UserDetails interface. This way, it's both a JPA managed type and a valid security user.
- Create the combined entity:
@Entity @Table(name = "app_users") public class AppUser implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; private boolean enabled; // Store authorities using @ElementCollection for a list of permissions @ElementCollection(fetch = FetchType.EAGER) private List<String> authorities; // Implement all UserDetails methods @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; // Adjust based on your business rules } @Override public boolean isAccountNonLocked() { return true; // Adjust based on your business rules } @Override public boolean isCredentialsNonExpired() { return true; // Adjust based on your business rules } @Override public boolean isEnabled() { return enabled; } // Getters, setters, and constructors here }
- Create the Repository:
public interface AppUserRepository extends CrudRepository<AppUser, Long> { Optional<AppUser> findByUsername(String username); }
- Update your
UserDetailsService(simpler now, no conversion needed):
@Service public class AppUserDetailsService implements UserDetailsService { private final AppUserRepository appUserRepository; public AppUserDetailsService(AppUserRepository appUserRepository) { this.appUserRepository = appUserRepository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return appUserRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); } }
Both options will resolve the "It's not a managed type" error, and they're far more maintainable than trying to force Spring Security's User class into a role it wasn't designed for. Pick the one that fits your POC's needs—Option 1 is great if you think you'll need to expand your database model independently of security later, while Option 2 is perfect for a simpler, more tightly integrated setup.
内容的提问来源于stack exchange,提问作者Kyle_jumpen

