Doru Chiulan
Doru Chiulan

Reputation: 316

Spring component scan doesn't load its own components if I change basePackages?

I'm trying to get started with Spring Boot so I can use it in my future projects.

However after I implemented my "authentication" library which contains some simple user service (implementation of UserDetailsService), I tried to integrate that into my new Spring Boot app.

Because the UserService is in a separate JAR, I had to change the base packages that Spring looks into so they get loaded.

Everything it's fine, my app starts, endpoints work as expected. I get redirected to login and I try to log in with my user from the database. Here the problem pops up. I get No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken.

After digging around I found out that if remove the base package classes to scan and use the inmemory UserDetailService everything works as expected. I want to use the default DaoAuthenticationProvider which should just use my UserDetailService implementation.

Can it be that Spring doesn't load this anymore when I change basePackages or I'm missing something else?

You can see what I tried here: (don't worry code is in Scala, I'm just evaluating how they work together)

@SpringBootApplication(scanBasePackages = Array("ro.sandd", "spring.framework"))
@EnableJpaRepositories(basePackages = Array("ro.sandd"))
@EntityScan(basePackages = Array("ro.sandd"))
class SpringDemoApplication

object SpringDemoApplication extends App {
    SpringApplication.run(classOf[SpringDemoApplication]);
}

@Configuration
@EnableWebSecurity
//@Import(Array(classOf[UserService]))
//@ComponentScan(basePackageClasses = Array(classOf[UserService], classOf[DaoAuthenticationProvider]))
class WebSecurityConfig(@Autowired val userService: UserService) extends WebSecurityConfigurerAdapter with Logging {

  protected override def configure(http: HttpSecurity): Unit = {

    http.csrf().disable().authorizeRequests()
      .antMatchers("/", "/demo").permitAll()
      .anyRequest().authenticated()
      .and()
      .formLogin()
      .loginProcessingUrl("/login")
      .permitAll()
      .and()
      .logout()
      .permitAll();
  }

  @Bean
  override def userDetailsService: UserDetailsService = {
    userService
  }
}

Also I have some simple Controller but I don't think it's worth to see the code. Also ... I probably can provide a custom provider, but my purpose is to use the existing one as for me it seems suitable.

PS Further investigation 1:

Note 1 - If I use @SpringBootApplication(scanBasePackageClasses = Array(classOf[UserService])) I can see the DaoAuthenticationProvider loaded into AuthenticationConfiguration. But my WebSecurityConfigurerAdapter config method is not called. Instead, the org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration$DefaultConfigurerAdapter gets used.

Note 2 - If I use @SpringBootApplication(scanBasePackageClasses = Array(classOf[UserService], classOf[WebSecurityConfig])) to force the load of my WebSecurityConfig, authenticationProviders list is empty.

So it must be some problem when this WebSecurityConfig actually gets used. However... I'm wondering how my UserDetailsService was working if this class wasn't used. That's where I provided it as you can see bellow. Probably Spring did some magic and injected it somewhere else.

 @Bean
  override def userDetailsService: UserDetailsService = {
    userService
  }

PS Further investigation 2:

I removed the @Bean declaration above and everything works as expected. Probably the fact that I already Autowire my UserService is enough for Spring to inject it where it has to.

Anyway, some explanation why it doesn't work when I use the override method would be useful.

Upvotes: 0

Views: 353

Answers (0)

Related Questions