spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.82k stars 5.9k forks source link

Update the Spring Security configuration class with Spring Boot 2.7.0 #11333

Closed fedegibut closed 2 years ago

fedegibut commented 2 years ago

My IDE reports two errors in the following class: 1.WebSecurityConfigurerAdapter is a deprecated class; 2.Could not autowire. No beans of 'DataSource' type found. Locally the code works but I would still like to update the class with the new specifications. The code "private DataSource dataSource;" by IntelliJ in red worries me a lot. How should I rewrite this class?

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
public class ConfigurazioneSpringSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    LivelliDeiRuoli livelliDeiRuoli;

    @Autowired
    GestioneUtentiSpringSecurity gestioneUtentiSpringSecurity;

    @Bean
    public static 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().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

        http.authorizeRequests().antMatchers(
                "/",
                "/login",
                "/benvenuto",
                "/registrazione",
                "/registrazione-eseguita",
                "/cookie",
                "/contatti",
                "logout"
        ).permitAll();

        // ... etc...

        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("/nome-pagina")
                .access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(2L) + "')");

        // ... etc...

    }

    @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();
    }

}

I updated the class by reading the following official documentation but in the page I found there are deprecated classes and methods.

https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

I think I have everything corrected but it remains to understand how to configure Spring Security so that the "remember my password" button works. I have marked the code with question marks. I use PostgreSQL as a database and I put the database connection parameters in the .properties files. Here is the new class code:

@Configuration
@EnableWebSecurity
public class ConfigurazioneSpringSecurity {

    @Autowired
    LivelliDeiRuoli livelliDeiRuoli;
    @Autowired
    GestioneUtentiSpringSecurity gestioneUtentiSpringSecurity;

    // ??? (code 1)
    @Autowired
    private DataSource dataSource;

    // ??? (code 2)
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
        db.setDataSource(dataSource);
        return db;
    }

    // ??? (code 3)
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public static BCryptPasswordEncoder metodoCrittografia() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void crittografiaPassword(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(gestioneUtentiSpringSecurity).passwordEncoder(metodoCrittografia());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        // I am in doubt whether to use the code above or the code below 
        // but the one above worked in the past and was not deprecated.
        // http.csrf().csrfTokenRepository(new HttpSessionCsrfTokenRepository()); ()

        // ??? (code 4)
        http.authorizeRequests().and()
                .rememberMe().tokenRepository(this.persistentTokenRepository())
                .tokenValiditySeconds(365 * 24 * 60 * 60);

        http.authorizeRequests().antMatchers(
                "/",
                "/login",
                "/benvenuto",
                "/registrazione",
                "/registrazione-eseguita",
                "/cookie",
                "/contatti",
                "logout"
        ).permitAll();

        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().antMatchers("/page1")
                .access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(1L) + "')");

        http.authorizeRequests().antMatchers("/page2")
                .access("hasAnyRole('" + livelliDeiRuoli.elencoRuoli(2L) + "')");

        // http. etc...

        http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated()).httpBasic(withDefaults());
        return http.build();
    }

}

With codes 1, 2, 3 and 4 I get a compile error: java: method does not override or implement a method from a supertype Without the codes 1, 2, 3 and 4 I get a WebApp startup error:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.0)

2022-05-31 14:51:58.455  INFO 3768 --- [  restartedMain] i.a.g.GestioneUtentiApplication          : Starting GestioneUtentiApplication using Java 11.0.15 on EB-DESKTOP with PID 3768 (...\target\classes ...)
2022-05-31 14:51:58.456  INFO 3768 --- [  restartedMain] i.a.g.GestioneUtentiApplication          : The following 1 profile is active: "sviluppo"
2022-05-31 14:51:58.495  INFO 3768 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2022-05-31 14:51:58.496  INFO 3768 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2022-05-31 14:51:59.690  INFO 3768 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https) 8080 (http)
2022-05-31 14:51:59.703  INFO 3768 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-05-31 14:51:59.703  INFO 3768 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.63]
2022-05-31 14:51:59.762  INFO 3768 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/it]     : Initializing Spring embedded WebApplicationContext
2022-05-31 14:51:59.762  INFO 3768 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1266 ms
2022-05-31 14:51:59.833  INFO 3768 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2022-05-31 14:51:59.967  INFO 3768 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2022-05-31 14:52:00.351  WARN 3768 --- [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'controlloPagineWeb': Unsatisfied dependency expressed through field 'authManager'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2022-05-31 14:52:00.352  INFO 3768 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-05-31 14:52:00.372  INFO 3768 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2022-05-31 14:52:00.375  INFO 3768 --- [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2022-05-31 14:52:00.385  INFO 3768 --- [  restartedMain] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-05-31 14:52:00.403 ERROR 3768 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field authManager in it.applicazionijava.gestioneutenti.pagine_web_spring_boot.ControlloPagineWeb required a bean of type 'org.springframework.security.authentication.AuthenticationManager' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:

Consider defining a bean of type 'org.springframework.security.authentication.AuthenticationManager' in your configuration.

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.437 s
[INFO] Finished at: 2022-05-31T14:52:00+02:00
[INFO] ------------------------------------------------------------------------

Process finished with exit code 0

I believe it is some bug in the framework. I also opened a Stackoverflow thread with no success.

https://stackoverflow.com/questions/72427751/update-the-spring-security-configuration-class-with-spring-boot-2-7-0

Thanks for the support Federico

rwinch commented 2 years ago

Thanks for getting in touch, but as you probably guessed since you also created a ticket on StackOverflow, it feels like this is a question that would be better suited to Stack Overflow. Please work with the community on finding an answer to your questions.

fedegibut commented 2 years ago

On the other site I get no answer.

fedegibut commented 2 years ago

Dear rwinch, This is not a stackoverflow question. I opened the discussion on that forum but I have not received any response. The documentation does not allow you to understand how to use your framework or your framework does not work and has some bugs. If you deprecate a class you should explain how to fix the framework code. I don't know if the problem is the documentation or the code. I didn't create Spring Security. I am also surprised not to see an answer from you. If you know Spring Security you can answer my question in seconds. If you do not want to answer this question because you consider me an unbearable or unpleasant person is another matter. I can't solve this problem myself because I didn't write Spring Security myself.

fedegibut commented 2 years ago

It has been 3 months since I opened the discussion. Nobody knows how to log in automatically after registration, or after password change or name change. Why don't the Spring Boot developers explain how to do this? Why is there no comprehensive technical documentation?

https://stackoverflow.com/questions/72427751/update-the-spring-security-configuration-class-with-spring-boot-2-7-3

With the old version of Spring Boot I added a very convenient login() method to the class marked with the @Controller annotation.

It seems to me a big mistake to mark a class as deprecated and not explain what is to be used in place of it.

    @Autowired
    private AuthenticationManager authManager;

    public void login(HttpServletRequest req, String user, String pass) {
        UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(user, pass);
        Authentication auth = authManager.authenticate(authReq);
        SecurityContext sc = SecurityContextHolder.getContext();
        sc.setAuthentication(auth);
    }

   // login after registratiom:
                    login(
                            request,
                            user,
                            password
                    );
   // with the new version of spring boot and spring security everything is an unknown... :(
fedegibut commented 2 years ago

Thanks for getting in touch, but as you probably guessed since you also created a ticket on StackOverflow, it feels like this is a question that would be better suited to Stack Overflow. Please work with the community on finding an answer to your questions.

@rwinch It has been 3 months since I opened the discussion!

Don't you think there is some problem with the new version or something important is missing from the documentation?

You dismissed my call for help right away.