I can't update my webapp to Spring Boot 2.6.0 (2.5.7 works but 2.6.0 doesn't)
Asked Answered
C

4

10

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;
        }
    }

}
Chancre answered 22/11, 2021 at 23:49 Comment(2)
Does this answer your question? Spring boot application fails to start after upgrading to 2.6.0 due to circular dependency[ unresolvable circular reference]Devine
See: #70037403Arleen
M
13

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.

Muck answered 23/11, 2021 at 0:4 Comment(8)
Magnificent! Thanks for the post. With your suggestion my webapp also works perfectly with Spring Boot 2.6.0. How can I upgrade my class to comply?Chancre
I would say that the issue might be with GestioneUtentiSpringSecurity gestioneUtentiSpringSecurity;. You will need to include its code so that we can check ;) Another possibility is public void crittografiaPassword(AuthenticationManagerBuilder auth) because you are also registering the public AuthenticationManager authenticationManagerBean() bean in the same class. You will have to try, but those are my guesses.Bedtime
Thank you very much, I added the code you asked for. If there is a better and more modern way to encrypt passwords, I am happy to change.Chancre
Have you checked this baeldung.com/… ?Bedtime
Thanks for your help. If I follow your advice I solve my problem but another problem appears. Persistent login stops working. I went to see the following link but the tutorial suggests writing some xml code. link If I upgrade to 2.6.0 without the specs in the properties file I am forced to write xml code and this upsets me a lot.Chancre
I would suggest you wait a bit until moving to 2.6. It was released very recently. Or you create a new question with this new issue and all the relevant details. If you do that, post here its link ;)Bedtime
From link above: If your application fails to start due to a BeanCurrentlyInCreationException you are strongly encouraged to update your configuration to break the dependency cycle.Luwian
I did as you advised me, we think that after all this time we will be luckier. #71446160Chancre
R
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);
        }
Reeve answered 18/3, 2022 at 13:9 Comment(0)
S
1

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

Stogy answered 5/4, 2022 at 4:24 Comment(0)
L
0

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())
            ...;
  }
Luwian answered 30/11, 2021 at 21:56 Comment(3)
You have an elaborated answer, but I have tried both options without luck. Which version of Spring are you using?Skirr
@KingMidas I've tested for 2.6.2Luwian
No luck then. I have going to spring.main.allow-circular-references solution.Skirr

© 2022 - 2024 — McMap. All rights reserved.