【问题标题】:SpringBoot, how to Authenticate with LDAP without using ldif?Spring Boot,如何在不使用 ldif 的情况下使用 LDAP 进行身份验证?
【发布时间】:2016-08-12 11:25:22
【问题描述】:

我正在尝试 SpringBoot here 中的 LDAP 身份验证示例

它使用了我认为不适用于我的要求的 ldif 方法,因为我们的 ldap 管理员不会告诉我在哪里可以找到我需要的 ldif。 在 springboot 之前,我曾经使用自己的 ldap 实现而不使用 ldif。有没有办法验证不使用 ldif 只是 SECURITY_AUTHENTICATION.simple ? 下面是我如何在基本 Java 没有 spring 中进行 ldap 安全性。我如何在春天做到这一点,而不使用 ldif 只是基本的用户名密码。

boolean isLdapRegistred(String username, String password) {
    boolean result = false;
    try {

        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://10.x.x.x:389");           
        env.put(Context.SECURITY_AUTHENTICATION, "simple");         
        env.put(Context.SECURITY_PRINCIPAL, "OUR-DOMAIN\\" + username);
        env.put(Context.SECURITY_CREDENTIALS, password);

        // Create the initial context
        DirContext ctx = new InitialDirContext(env);
        result = ctx != null;
        if (ctx != null)
        ctx.close();
        System.out.println(result);
        return result;
    } catch (Exception e) {
        System.out.println("oops");
        return result;
    }

}

以下是 SpringBoots 示例需要使用我的凭据而不是 ldif。

@Configuration
protected static class AuthenticationConfiguration extends
        GlobalAuthenticationConfigurerAdapter {

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .ldapAuthentication()
                .userDnPatterns("uid={0},ou=people")
                .groupSearchBase("ou=groups")
                .contextSource().ldif("classpath:test-server.ldif");
    }
}

【问题讨论】:

  • 你试过.contextSource().url("ldap://10.x.x.x").port("389")而不是.contextSource().ldif("classpath:test-server.ldif");吗?
  • 仅供参考,@jny,port() 方法的输入参数是 int,而不是 String。

标签: java spring spring-security spring-boot


【解决方案1】:

这个对我来说效果很好,但我需要对其进行微小的修改。

    @Configuration
    @EnableWebSecurity
    public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(ldapAuthenticationProvider());
        }

        @Bean
        public AuthenticationProvider ldapAuthenticationProvider() throws Exception {
            DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
Arrays.asList("ldapServerUrl:port"),rootDn);
            contextSource.afterPropertiesSet();
            LdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(ldapUserSearchBase, ldapUserSearchFilter, contextSource);
            BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource);
            bindAuthenticator.setUserSearch(ldapUserSearch);
            LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, new DefaultLdapAuthoritiesPopulator(contextSource, ldapGroupSearchBase));
            return ldapAuthenticationProvider;
        }
    }

在达到这一点之前我已经痛苦了好几天 否则,您可以使用自定义身份验证并进行类似的操作

    @Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private Logger log = Logger.getLogger(CustomAuthenticationProvider.class);

    @Override
    public Authentication authenticate(Authentication authentication) 
      throws AuthenticationException {

        String email = authentication.getName();
        String password = authentication.getCredentials().toString();

        log.info("email : " + email);
        log.info("password : " + password);

        try {
            if (authenticate(email, password)) {

                // use the credentials
                // and authenticate against the third-party system
                return new UsernamePasswordAuthenticationToken(
                        email, password, new ArrayList<>());
            } else {
                return null;
            }
        } catch (NamingException ex) {
            log.info(ex);
        }
        return null;
    }

    boolean isLdapRegistred(String username, String password) {
    boolean result = false;
    try {

        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://10.x.x.x:389");           
        env.put(Context.SECURITY_AUTHENTICATION, "simple");         
        env.put(Context.SECURITY_PRINCIPAL, "OUR-DOMAIN\\" + username);
        env.put(Context.SECURITY_CREDENTIALS, password);

        // Create the initial context
        DirContext ctx = new InitialDirContext(env);
        result = ctx != null;
        if (ctx != null)
        ctx.close();
        System.out.println(result);
        return result;
    } catch (Exception e) {
        System.out.println("oops");
        return result;
    }

}


    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(
          UsernamePasswordAuthenticationToken.class);
    }
}

在另一个班级

    @Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    private Logger log = Logger.getLogger(WebSecurityConfiguration.class);
    @Autowired
    private CustomAuthenticationProvider authProvider;

    @Override
    protected void configure(
      AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(authProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
            .and()
            .httpBasic();
    }
}

然后奇迹发生了

【讨论】:

  • 谢谢,帮我把“if (authenticate(email,password))”改成“(isLdapRegistred(email,password))”
【解决方案2】:

没有 LDIF,而使用 Spring,您可以执行以下操作:

@Configuration
@EnableWebSecurity
public class HttpSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(ldapAuthenticationProvider());
    }

    @Bean
    public AuthenticationProvider ldapAuthenticationProvider() throws Exception {
        DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapServerUrl);
        contextSource.setUserDn(ldapManagerDn);
        contextSource.setPassword(ldapManagerPassword);
        contextSource.afterPropertiesSet();
        LdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch(ldapUserSearchBase, ldapUserSearchFilter, contextSource);
        BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource);
        bindAuthenticator.setUserSearch(ldapUserSearch);
        LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator, new DefaultLdapAuthoritiesPopulator(contextSource, ldapGroupSearchBase));
        return ldapAuthenticationProvider;
    }
}

【讨论】:

    【解决方案3】:

    没有 ldif 你可以这样使用:

    1. 将以下参数添加到application.properties
    ad.domain=domain.name
    ad.url=ldap://url:port
    
    1. 创建 securityconfig 类扩展 WebSecurityConfigurerAdapter 像这样:
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.ProviderManager;
    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.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
        
    import java.util.Arrays;
    import org.springframework.beans.factory.annotation.Value;
        
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        
        @Value("${ad.domain}")
        private String AD_DOMAIN;
        
        @Value("${ad.url}")
        private String AD_URL;
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
             http.authorizeRequests()
                 .anyRequest()
                 .fullyAuthenticated()
                 .and()
                 .formLogin();
        }
        
        @Override
        protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
                authManagerBuilder.authenticationProvider(activeDirectoryLdapAuthenticationProvider()).userDetailsService(userDetailsService());
        }
        
        @Bean
        public AuthenticationManager authenticationManager() {
            return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
        }
        
        @Bean
        public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
            ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(AD_DOMAIN, AD_URL);
            provider.setConvertSubErrorCodesToExceptions(true);
            provider.setUseAuthenticationRequestCredentials(true);
            return provider;
        }
    }
    

    一切正常。我的意思是,如果用户名和密码错误,你会通过 spring boot 本身得到这个警告。

    ActiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: Supplied password was invalid
    

    如果您输入正确,则取决于您重定向到哪个页面。

    享受吧!

    【讨论】:

      【解决方案4】:

      如果有人对相应的 Spring Boot 配置感兴趣,这对我有用(我试图删除不相关的细节):

      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Override
          public void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth
                      .ldapAuthentication()
                      .contextSource()
                      .url("ldap://hostname:389/dc=myapp,dc=mycompany,dc=com")
                      .and()
                      .userDnPatterns("cn={0},cn=Users");
          }
      }
      

      以上代码对应如下 ldapsearch 命令(他们经常建议使用 ldapsearch 来尝试 base DN 和 userDnPatterns 的不同路径配置:

      ldapsearch -H "ldap://hostname:389" -D "cn=someuser,cn=Users,dc=myapp,dc=mycompany,dc=com" -W -b "dc=myapp,dc=mycompany,dc=com" 'cn=someuser,cn=Users,dc=myapp,dc=mycompany,dc=com'
      
      • -W 以交互方式询问密码
      • -H 指定主机:端口
      • -D cn - 指示ldapsearch专门绑定到这个地址(cn) - 据我了解,这对应于用这个CN登录LDAP,只要给下面提示的密码就可以了
      • -b ... 指定将过滤结果的基本 DN
      • 非常重要的是不要忘记上面单引号中的最后一部分(单引号或双引号,我认为这没有任何区别),因为这指定了 ldapsearch 在绑定/登录后将执行的查询 - 如果你不这样做'如果不指定任何内容,它将尝试搜索所有条目,并可能导致 LDAP 服务器无响应

      最后,Spring Boot 安全配置中的 {0} 是来自 ldapsearch 的“someuser”所在的位置,即 Spring 将根据 userDnPatterns 中给出的内容构造要绑定的地址,将 {0} 替换为登录期间指定的任何用户名

      PS。这适用于旧版本的 Oracle Internet Directory,我们想要验证的所有用户都在 cn=Users 下,而不是例如ou=people 在相应的 Spring Boot LDAP 教程中(我猜)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-22
        • 2022-01-20
        • 2021-09-02
        • 2020-07-06
        • 2018-08-09
        • 1970-01-01
        • 2020-05-22
        • 2020-08-07
        相关资源
        最近更新 更多