他の場所でJdbcUserDetailsManagerを構成したのに、なぜInMemoryUserDetailsManagerがこのコントローラーに注入されるのですか?

2020-03-24 spring-boot spring-security

Spring Boot WebアプリでSpring Security JDBC認証を使用しようとしています。 これは(かなり単純化された、関連する)構成です。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;

import javax.sql.DataSource;

@Configuration
public class SecurityConfiguration {
    @Autowired
    private DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        JdbcUserDetailsManagerConfigurer jdbcUserDetailsManagerConfigurer = auth.jdbcAuthentication()
                .dataSource(dataSource)
                .withDefaultSchema();
    }
}

これがコントローラーです:

import org.adventure.inbound.UserFormData;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class UserController {
    private final UserDetailsManager userDetailsManager;

    public UserController(UserDetailsManager userDetailsManager) {
        this.userDetailsManager = userDetailsManager;
    }

    @PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public ResponseEntity<Void> registerUser(UserFormData userFormData) {
        userDetailsManager.createUser(
                User
                        .withUsername(userFormData.getUsername())
                        .password(userFormData.getPassword())
                        .authorities(new SimpleGrantedAuthority("ROLE_USER"))
                        .build());
        return ResponseEntity.created(null).build();
    }
}

アプリを起動すると、次のことがわかります。

  • AuthenticationManagerBuilderはJDBCUDMを構成します。

ここに画像の説明を入力してください

しかし、おそらくいくつかのステップが欠落していますか?

  • InMemoryUserDetailsManagerは、Spring Securityのデフォルトユーザーでインスタンス化されます。

ここに画像の説明を入力してください

  • コントローラーは、インスタンス化されると、InMemoryUserDetailsManagerを受け取ります。

ここに画像の説明を入力してください

つまり、新しいユーザーを作成しようとすると、使用したいJDBCUDMではなくInMemoryUDMが使用されます。何故ですか?

実用的なソリューション

JdbcUserDetailsManagerConfigurerを介してUserDetailsS​​erviceをすでに設定していますが、Beanとして公開していません。

    @Autowired
    @Bean
    public UserDetailsManager configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        JdbcUserDetailsManagerConfigurer jdbcUserDetailsManagerConfigurer = auth.jdbcAuthentication()
                .dataSource(dataSource)
                .withDefaultSchema();
        return jdbcUserDetailsManagerConfigurer.getUserDetailsService();
    }

Answers

あなたのSecurityConfigurationはWebSecurityConfigurerAdapterから延びる作り、@EnableWebSecurityを追加し@BeanとしてあなたJdbcUserDetailsManagerを公開

@Configuration
@EnableWebSecurity
public class SecurityConfiguration
   extends WebSecurityConfigurerAdapter {

    @Bean
    public JdbcUserDetailsManager userDetailsManager() {
        return yourJdbcUserDetailsManager;
    }

/// etc.
}

構成でuserDetailsServiceBean()オーバーライドし、スーパーメソッドを呼び出して@Beanアノテーションを追加し、構成されたサービスを利用できるようにします。これは、Javadocでも説明れています。

@Configuration
public class SecurityConfiguration extends WebSecutiryConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        JdbcUserDetailsManagerConfigurer jdbcUserDetailsManagerConfigurer = auth.jdbcAuthentication()
                .dataSource(dataSource)
                .withDefaultSchema();
    }

    @Bean
    public UserDetailsService userDetailsServiceBean()
                                          throws java.lang.Exception {
        return super.userDetailsServiceBean();
    } 
}

また、Spring Boot構成の代わりに構成を使用するため、 @EnableWebSecurityを追加してSpring Bootのデフォルトを無効にする必要がある場合があります。

Related