Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

}
like image 978
Federico Galimberti Avatar asked Nov 22 '21 23:11

Federico Galimberti


People also ask

Which version of spring does spring boot 2.6 use?

Spring Boot 2.6 moves to new versions of several Spring projects: Spring Data 2021.1. Spring HATEOAS 1.4. Spring AMQP 2.4.

How do I update spring boot apps?

Upgrading the Spring Boot CLI To upgrade an existing CLI installation, use the appropriate package manager command (for example, brew upgrade ). If you manually installed the CLI, follow the standard instructions, remembering to update your PATH environment variable to remove any older references.

What is the latest version of spring and spring boot?

The current stable version, as of July 2022, is Spring 5.3. 22. You can always find new version announcements on https://github.com/spring-projects/spring-framework/releases.


3 Answers

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.

like image 194
João Dias Avatar answered Oct 18 '22 03:10

João Dias


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);
        }
like image 35
Panagiotis Bougioukos Avatar answered Oct 18 '22 01:10

Panagiotis Bougioukos


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

like image 1
amuniz Avatar answered Oct 18 '22 01:10

amuniz