Аннотации метода безопасности с конфигурацией Java и Spring Безопасность 3.2

У меня возникают некоторые проблемы с настройкой моего приложения с помощью аннотации уровня метода, управляемой @EnableGlobalMethodSecurity Я использую инициализацию стиля Serlet 3.0 с помощью

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(MultiSecurityConfig.class);
    }
}

Я попытался выполнить два разных способа инициализации AuthenticationManager как со своими проблемами. Обратите внимание, что не используя @EnableGlobalMethodSecurity приводит к успешному запуску сервера, и вся защита формы выполняется, как ожидалось. Мои проблемы возникают, когда я добавляю аннотации @EnableGlobalMethodSecurity и @PreAuthorize("hasRole('ROLE_USER')") на моем контроллере.

Я пытаюсь самостоятельно установить защиту на основе форм и api. Аннотации, основанные на методе, должны работать только для безопасности api.

Одна конфигурация была следующей.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/api/**").httpBasic();
        }

        protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER", "ADMIN");
        }
    }

    @Configuration
    public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/static/**","/status");
        }

        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().hasRole("USER").and()
                .formLogin().loginPage("/login").permitAll();
        }

        protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER", "ADMIN");
        }
    }

}

Это не идеально, поскольку я действительно хочу только одну регистрацию механизма аутентификации, но главная проблема заключается в том, что это приводит к следующему исключению:

java.lang.IllegalArgumentException: Expecting to only find a single bean for type interface org.springframework.security.authentication.AuthenticationManager, but found []

Насколько мне известно, @EnableGlobalMethodSecurity устанавливает свой собственный AuthenticationManager, поэтому я не уверен, что проблема здесь.

Вторая конфигурация следующая.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {

    @Bean
    protected AuthenticationManager authenticationManager() throws Exception {
        return new AuthenticationManagerBuilder(ObjectPostProcessor.QUIESCENT_POSTPROCESSOR)
                .inMemoryAuthentication()
                    .withUser("user").password("password").roles("USER").and()
                    .withUser("admin").password("password").roles("USER", "ADMIN").and()
                    .and()
                .build();
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        @Override protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/api/**").httpBasic();
        }
    }

    @Configuration
    public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/static/**","/status");
        }

        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().hasRole("USER").and()
                .formLogin().loginPage("/login").permitAll();
        }
    }

}

Эта конфигурация фактически запускается успешно, но с исключением

java.lang.IllegalArgumentException: A parent AuthenticationManager or a list of AuthenticationProviders is required
at org.springframework.security.authentication.ProviderManager.checkState(ProviderManager.java:117)
at org.springframework.security.authentication.ProviderManager.<init>(ProviderManager.java:106)
at org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder.performBuild(AuthenticationManagerBuilder.java:221)

и когда я тестирую, я обнаружил, что безопасность не работает.

Я смотрел на это уже пару дней и даже после погружения в код реализации безопасности spring я не могу найти, что не так с моей конфигурацией.

Я использую spring -security-3.2.0.RC1 и spring -framework-3.2.3.RELEASE.

Ответ 1

Когда вы используете методы protected registerAuthentication на WebSecurityConfigurerAdapter, он просматривает аутентификацию на WebSecurityConfigurerAdapter, поэтому EnableGlobalMethodSecurity не может ее найти. Если вы думаете об этом... это имеет смысл, поскольку метод защищен.

Ошибка, которую вы видите, на самом деле является отладчиком (обратите внимание, что уровень DEBUG). Причина в том, что Spring Security попробует несколько способов автоматического подключения глобальной безопасности методов. В частности, EnableGlobalMethodSecurity попробует следующие способы попробовать AuthenticationManager:

  • Если вы расширяете GlobalMethodSecurityConfiguration и переопределяете registerAuthentication, он будет использовать переданный AuthenticationManagerBuilder. Это позволяет изолировать AuthenticationManager так же, как вы можете сделать это с помощью WebSecurityConfigurerAdapter
  • Попробуйте создать из глобального общего экземпляра AuthenticationManagerBuilder, если он не работает, он регистрирует сообщение об ошибке, которое вы видите (обратите внимание, что в журналах также указано "Это нормально, мы попробуем напрямую использовать AuthenticationManager" )
  • Попробуйте использовать AuthenticationManager, который отображается как bean.

Для вашего кода вам будет лучше использовать что-то вроде следующего:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MultiSecurityConfig {
    // Since MultiSecurityConfig does not extend GlobalMethodSecurityConfiguration and
    // define an AuthenticationManager, it will try using the globally defined
    // AuthenticationManagerBuilder to create one

    // The @Enable*Security annotations create a global AuthenticationManagerBuilder 
    // that can optionally be used for creating an AuthenticationManager that is shared
    // The key to using it is to use the @Autowired annotation
    @Autowired
    public void registerSharedAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER", "ADMIN");
    }

    @Configuration
    @Order(1)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        // Since we didn't specify an AuthenticationManager for this class,
        // the global instance is used


        protected void configure(HttpSecurity http) throws Exception {
            http
                .antMatcher("/api/**")
                .httpBasic();
        }
    }

    @Configuration
    public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
        // Since we didn't specify an AuthenticationManager for this class,
        // the global instance is used

        public void configure(WebSecurity web) throws Exception {
            web
                .ignoring()
                    .antMatchers("/static/**","/status");
        }

        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().hasRole("USER")
                    .and()
                .formLogin()
                    .loginPage("/login")
                    .permitAll();
        }
    }

}

ПРИМЕЧАНИЕ. Дополнительная документация по этому вопросу будет добавлена ​​к ссылке в ближайшие дни.