I am trying to implement a DAO based authentication in an application using Spring Security.
When I tried to log in to the application with a user I got this error:
failed to lazily initialize a collection of role: com.example.app.dao.User.groups, could not initialize proxy - no Session
Looking at @jcmwright80 's answer to this question I understood that I should ideally annotate UserDetailsServiceImpl class as @Transactional. After doing that I got an error during login:
Bean named 'userDetailsService' is expected to be of type 'com.example.app.service.UserDetailsServiceImpl' but was actually of type 'com.sun.proxy.$Proxy238'"}}
This seems to be an issue related to the proxy object created on UserDetailsServiceImpl - how can I fix this gracefully?
CODE
Relevant part of the security configuration:
@Configuration
@ComponentScan("com.example.app.service")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
UserDetailsServiceImpl.java
@Service
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService{
public UserDetailsServiceImpl () {};
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.getUser(username);
if (user == null) {
throw new UsernameNotFoundException ("User not found.");
}
return new UserDetailsImpl(user);
}
}
User.java
@Entity
@Table(name="users",schema="sec")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="userGen")
@SequenceGenerator(name="userGen", sequenceName="user_id_seq", schema="sec")
private long id;
// Validation constraints on the fields ...
private String username;
private String password;
private boolean enabled;
@ManyToMany
@JoinTable(name="group_members", schema="sec", joinColumns= { @JoinColumn(name="user_id") }, inverseJoinColumns = { @JoinColumn(name="group_id") } )
private Set<Group> groups;
// Getters, Setters etc. ...
}
(The alternative solution of using @ManyToMany(fetch = FetchType.EAGER) on collection type fields in User and Group classes works, though it could impact performance.)