【问题标题】:Provider order using AuthenticationManagerBuilder使用 AuthenticationManagerBuilder 的提供程序订单
【发布时间】:2015-09-26 22:19:04
【问题描述】:

我正在使用 Spring Security 4.0.1 并希望使用多个身份验证提供程序来使用基于 Java 的配置进行身份验证。如何指定提供者顺序?

我希望使用AuthenticationManagerBuilder,因为这是WebSecurityConfigurerAdapter.configureGlobal() 公开的内容,但我看不到任何指定顺序的方法。我需要手动创建ProviderManager吗?

更新:这是基于 Arun 回答的问题说明。我要使用的特定提供程序是 ActiveDirectoryLdapAuthenticationProviderDaoAuthenticationProvider 用于自定义 UserService

最后,我想先验证DaoAuthenticationProvider,然后再验证ActiveDirectoryLdapAuthenticationProvider

AD 提供程序涉及对AuthenticationManagerBuilder.authenticationProvider() 的调用,但 DAO 提供程序涉及对AuthenticationManagerBuilder.userService() 的调用,这会在幕后围绕用户服务创建一个DaoAuthenticationProvider。查看源代码,它没有直接将提供程序放在提供程序列表中(它创建了一个配置器),所以 Arun 的答案在这里对我不起作用。

我尝试手动创建DaoAuthenticationProvider 并将其传递给authenticationProvider()。对订单没有影响。

【问题讨论】:

  • 我可以看看你的 spring 安全配置代码吗?我想看看身份验证提供程序是如何配置的

标签: java spring authentication spring-security


【解决方案1】:

我在 Spring 5 应用程序中遇到了完全相同的问题,但在我的情况下,创建 DaoAuthenticationProvider 有帮助。这是我的代码(我省略了很多并粘贴了最重要的代码)。

...
import javax.annotation.PostConstruct;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);

    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    private final UserDetailsService userDetailsService;

    private final TokenProvider tokenProvider;

    private final CorsFilter corsFilter;

    private final SecurityProblemSupport problemSupport;

    public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService, TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
        this.authenticationManagerBuilder = authenticationManagerBuilder;
        this.userDetailsService = userDetailsService;
        this.tokenProvider = tokenProvider;
        this.corsFilter = corsFilter;
        this.problemSupport = problemSupport;
    }

    @PostConstruct
    public void init() {
        try {
            authenticationManagerBuilder.authenticationProvider(userDetailsAuthenticationProvider());

            if (isSsoEnabled()) {
                authenticationManagerBuilder
                    .authenticationProvider(activeDirectoryLdapAuthenticationProvider());
            }

            if (isKerberosEnabled()) {
                authenticationManagerBuilder
                    .authenticationProvider(kerberosServiceAuthenticationProvider());
            }

        } catch (Exception e) {
            throw new BeanInitializationException("Security configuration failed", e);
        }
    }

    @Bean
    public DaoAuthenticationProvider userDetailsAuthenticationProvider() {
        DaoAuthenticationProvider authProvider
            = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new CustomPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        ...
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    ...
    }

    private JWTConfigurer securityConfigurerAdapter() {
        return new JWTConfigurer(tokenProvider);
    }

    private ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        ActiveDirectoryLdapAuthenticationProvider provider =
            new ActiveDirectoryLdapAuthenticationProvider(getAdDomain(), getAdServer());
        provider.setUserDetailsContextMapper((UserDetailsContextMapper) userDetailsService);
        return provider;
    }


    private boolean isSsoEnabled() {
        return Boolean.parseBoolean(ConfigurationFileUtils.getConfigurationProperty("security.use-sso"));
    }

    private boolean isKerberosEnabled() {
        return isSsoEnabled() && Boolean.parseBoolean(ConfigurationFileUtils.getConfigurationProperty("security.use-kerberos"));
    }    
}

【讨论】:

    【解决方案2】:

    我在 configure 方法中尝试了一个 objectPostProcessor,它起作用了。不确定这是否是您想要的:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.jdbcAuthentication().dataSource(dataSource)
               .passwordEncoder(new BCryptPasswordEncoder());
          auth.authenticationProvider(new CustomAuthenticationProvider(this.dataSource));
    
          auth.objectPostProcessor(new ObjectPostProcessor<Object>() {
            @Override
            public <O> O postProcess(O object) {
                ProviderManager providerManager = (ProviderManager) object;
                Collections.swap(providerManager.getProviders(), 0, 1);
                return object;
            }
        });
    }
    

    这是 WebSecurityConfigurerAdapter 继承类的 configure 方法。

    对象后处理器的原因是我们需要等待 AuthenticationManagerBuilder 真正构建对象,然后才能访问和更改提供者列表的顺序。

    希望对您有所帮助。如果您有任何问题,请告诉我。

    【讨论】:

    • 是的,它有效,你可以应用它,当你使用authenticationManagerBuilder .ldapAuthentication() 时(当 ldap 在最后一个地方,你不能强制它在自定义身份验证提供程序之前)
    【解决方案3】:

    没有明确的订购规定。调用顺序将是您将AuthenticationProvider 提供给AuthenticationManagerBuilder.authenticationProvider() 的顺序。 xml配置参考here。这同样适用于 java 配置。

    例如

    auth.authenticationProvider(getAuthenticationProvider2());
    auth.authenticationProvider(getAuthenticationProvider1());
    

    将导致以下调用顺序AuthenticationProvider2,AuthenticationProvider1

     auth.authenticationProvider(getAuthenticationProvider1());
     auth.authenticationProvider(getAuthenticationProvider2());
    

    将导致以下调用顺序AuthenticationProvider1,AuthenticationProvider2

    【讨论】:

    • 谢谢阿伦。我查看了代码,可以看到这适用于 AuthenticationProviders 我们明确地通过 authenticationProvider() 方法,所以 +1。但我试图让它与 UserService 一起工作。我会相应地更新问题。
    猜你喜欢
    • 2019-04-23
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    • 2014-01-21
    • 2017-02-06
    • 2019-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多