'I can't update my webapp to Spring Boot 2.6.0 (2.5.7 works but 2.6.0 doesn't)
As mentioned in the title I can't update my webapp to Spring Boot 2.6.0. I wrote my webapp using Spring Boot 2.5.5 and everything works perfectly. If I update the pom.xml file with this new tag:
<version>2.5.7</version>
My webapp works perfectly. All tests work. If I perform this update the webapp does not start:
<version>2.6.0</version>
Starting the DEBUG mode the IDE shows me an error and 2 links to 2 classes of my webapp.
2021-11-23 00:31:45.419 ERROR 21884 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'configurazioneSpringSecurity': Requested bean is currently in creation: Is there an unresolvable circular reference?
It seems the problem is in this class:
@Configuration
@EnableWebSecurity
public class ConfigurazioneSpringSecurity extends WebSecurityConfigurerAdapter {
@Autowired
LivelliDeiRuoli livelliDeiRuoli;
@Autowired
GestioneUtentiSpringSecurity gestioneUtentiSpringSecurity;
@Bean
public BCryptPasswordEncoder metodoCrittografia() {
return new BCryptPasswordEncoder();
}
@Autowired
public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(gestioneUtentiSpringSecurity).passwordEncoder(metodoCrittografia());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers(
"/",
"/login",
"/benvenuto",
"/registrazione",
"/registrazione-eseguita",
"/pagine-applicazione"
).permitAll();
http.authorizeRequests().antMatchers("/area-riservata")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(1L) + "')");
http.authorizeRequests().antMatchers("/cambio-password")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(1L) + "')");
http.authorizeRequests().antMatchers("/cambio-nome")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(1L) + "')");
http.authorizeRequests().antMatchers("/cancella-utente")
.access("isAuthenticated()");
http.authorizeRequests().antMatchers("/gestione-utenti")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(2L) + "')");
http.authorizeRequests().antMatchers("/gestione-ruoli")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(3L) + "')");
http.authorizeRequests().antMatchers("/pannello-di-controllo")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(3L) + "')");
http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/errore-403");
http.authorizeRequests().and().formLogin()
.loginProcessingUrl("/pagina-login")
.loginPage("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?errore=true")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutUrl("/pagina-logout")
.logoutSuccessUrl("/login?logout=true");
http.authorizeRequests().and() //
.rememberMe().tokenRepository(this.persistentTokenRepository()) //
.tokenValiditySeconds(365 * 24 * 60 * 60);
http.authorizeRequests().antMatchers("/gestione-eventi")
.access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(2L) + "')");
http.authorizeRequests().antMatchers(
"/cerca-eventi",
"/ultimi-eventi"
).permitAll();
}
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
db.setDataSource(dataSource);
return db;
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
or in this:
@SpringBootApplication
@Profile("sviluppo")
public class GestioneUtentiApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(GestioneUtentiApplication.class);
}
public static void main(String[] args) {
System.setProperty("server.servlet.context-path", "/gestioneutenti");
SpringApplication.run(GestioneUtentiApplication.class, args);
}
}
What's wrong with these classes?
What changes with Spring Boot 2.6.0?
GestioneUtentiSpringSecurity implements UserDetailsService:
@Service
public class GestioneUtentiSpringSecurity implements UserDetailsService {
@Autowired
private UtenteRepository utenteRepository;
@Autowired
private RuoloRepository ruoloRepository;
@Autowired
EseguiVariabiliDiSistema eseguiVariabiliDiSistema;
@Autowired
LivelliDeiRuoli livelliDeiRuoli;
@Override
public UserDetails loadUserByUsername(String nomeUtente) throws UsernameNotFoundException {
Utente utente = trovaUtenteConPrivilegiDiAutenticazione(nomeUtente);
if (utente == null) {
throw new UsernameNotFoundException("L'utente " + nomeUtente + " non è stato trovato nel database.");
}
List<String> ruoliUtente = null;
try {
ruoliUtente = this.ruoloRepository.trovaRuoliUtente(utente.getId());
}catch (Exception b){
ruoliUtente = null;
}
List<GrantedAuthority> grantList = null;
try{
grantList = new ArrayList<GrantedAuthority>();
if (ruoliUtente != null) {
for (String ruolo : ruoliUtente) {
GrantedAuthority authority = new SimpleGrantedAuthority(ruolo);
grantList.add(authority);
}
}
}catch (Exception c){
grantList = null;
}
UserDetails userDetails = null;
if((utente != null) && (ruoliUtente != null) && (grantList != null)){
userDetails = (UserDetails) new User(utente.getNome(), utente.getPassword(), grantList);
}
return userDetails;
}
public Utente trovaUtenteConPrivilegiDiAutenticazione(String nomeUtente){
try{
Utente utente = utenteRepository.trovaUtente(nomeUtente);
if(livelliDeiRuoli.requisitiUtenteConRuoloMassimo(utente)){
return utente;
} else{
eseguiVariabiliDiSistema.trovaVariabileSenzaVerificaUtente(
new VariabileSistema(0L, "login", "")
);
if(eseguiVariabiliDiSistema.getVariabileDiSistema().getValore().equals("true")){
return utente;
}else if(eseguiVariabiliDiSistema.getVariabileDiSistema().getValore().equals("false")){
return null;
}else{
return null;
}
}
}catch (Exception e){
return null;
}
}
}
Solution 1:[1]
Starting on Spring Boot 2.6, circular dependencies are prohibited by default. you can allow circular references again by setting the following property:
spring.main.allow-circular-references = true
You can read some more details about this in the Spring Boot 2.6 Release Notes.
Solution 2:[2]
The problem that Spring faces here and causes to not able to move forward starting from spring boot 2.6 with the default configuration of spring.main.allow-circular-references = false
is located in the following part
@Bean
public BCryptPasswordEncoder metodoCrittografia() {
return new BCryptPasswordEncoder();
}
@Autowired
public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(gestioneUtentiSpringSecurity).passwordEncoder(metodoCrittografia());
}
I believe this is happening because the WebSecurityConfig extends WebSecurityConfigurerAdapter
has some circular references in combination with BCryptPasswordEncoder
inside this class.
The solution is to create another configuration class, where you can split the configurations so that spring is able to correctly create the beans avoiding circular references.
So you can create the following extra class
@Configuration
public class CustomSecurityConfig {
@Bean
public BCryptPasswordEncoder metodoCrittografia() {
return new BCryptPasswordEncoder();
}
}
Then in your original ConfigurazioneSpringSecurity.class
you can replace the failing
@Bean
public BCryptPasswordEncoder metodoCrittografia() {
return new BCryptPasswordEncoder();
}
@Autowired
public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(gestioneUtentiSpringSecurity).passwordEncoder(metodoCrittografia());
}
with the
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(gestioneUtentiSpringSecurity)
.passwordEncoder(passwordEncoder);
}
Solution 3:[3]
Although setting the application.properties works, it is likely using a feature that is going to be deprecated. I was able to work around this by using setter based injection. It's a bit more verbose but might be a good starting point for those looking to stay current and not use features that might be deprecated down the line.
It's certainly an answer that can be improved upon and I hope others can contribute perhaps more concise answers. I'll update this if I find anything cleaner.
Before
@Component
public class CustomFilter extends OncePerRequestFilter {
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
private JWTUtils jwtUtils;
//When any api will be called this method will be called first and this will extract
// Token from header pass to JWT Util calls for token details extraction
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
//implementation
}
}
After
@Component
public class CustomFilter extends OncePerRequestFilter {
private MyUserDetailsService myUserDetailsService;
public void setMyUserDetailsService(MyUserDetailsService myUserDetailsService) {
this.myUserDetailsService = myUserDetailsService;
}
public void setJwtUtils(JWTUtils jwtUtils) {
this.jwtUtils = jwtUtils;
}
private JWTUtils jwtUtils;
//When any api will be called this method will be called first and this will extract
// Token from header pass to JWT Util calls for token details extraction
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException {
//implementation
}
}
reference: https://theintuitiveprogrammer.com/post-eight.html
Solution 4:[4]
I've this problem during migrate to spring boot 2.6.x with WebSecurityConfig
code:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService userDetailsService() {
return email -> {
....
};
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
...;
}
fix according WebSecurityConfigurerAdapter#userDetailsServiceBean
javadoc:
Override this method to expose a UserDetailsService
created from configure(AuthenticationManagerBuilder)
as a bean ...
To change the instance returned, developers should change userDetailsService()
instead
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
@Override
public UserDetailsService userDetailsService() {
return email -> {
....
};
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
...;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | João Dias |
Solution 2 | Panagiotis Bougioukos |
Solution 3 | amuniz |
Solution 4 |