java SpringBoot,如何在不使用 ldif 的情况下使用 LDAP 进行身份验证?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36732689/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 01:49:09  来源:igfitidea点击:

SpringBoot, how to Authenticate with LDAP without using ldif?

javaspringspring-securityspring-boot

提问by TwoThumbSticks

I am trying out the LDAP Authentication example in SpringBoot here

我在这里尝试 SpringBoot 中的 LDAP 身份验证示例

It is using the ldif approach which I think is not applicable to my requirements because our ldap admin wont tell me where to find the ldif that I need. Before springboot I used to use my own ldap implementation not using ldif. Is there a way to validate not using ldif just the SECURITY_AUTHENTICATION.simple ? Below is how I do ldap security in basic Java no spring. How do I do this in spring without using ldif just basic username password.

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

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;
    }

}

Below is SpringBoots example need to use my credentials instead of ldif.

下面是 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");
    }
}

回答by Emilien Brigand

Without LDIF, and using Spring, you can do something like:

如果没有 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;
    }
}

回答by Ali Miskeen

this one has worked perfectly for me but I need to make tiny modifications to it.

这个对我来说非常有效,但我需要对它进行微小的修改。

    @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;
        }
    }

I have suffered for days before getting to this point Other wise you can use custom authentication and make the like this

在达到这一点之前我已经受苦了好几天否则你可以使用自定义身份验证并像这样

    @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);
    }
}

and on another 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();
    }
}

Then the magic happens

然后奇迹发生了

回答by hello_earth

in case somebody is interested in corresponding Spring Boot configuration, this is what worked for me (i tried to remove irrelevant details):

如果有人对相应的 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");
    }
}

the above code corresponds to the following ldapsearch command (they often suggest to use ldapsearch to tryout different path configurations for base DN and userDnPatterns:

上面的代码对应下面的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 asks for password interactively
  • -H specifies host:port
  • -D cn - instructs ldapsearch to bind specifically to this address (cn) - as far as i understood, this corresponds to logging into LDAP with this CN, as far as the password given to the following prompt is alright
  • -b ... specifies base DN that will filter the results
  • it is very important to not forget the last part in single quotes above (single or double i don't think this makes any difference) because this specifies the query that ldapsearch will execute after binding/logging in - if you don't specify anything, it will try to perform search of ALL entries and may render LDAP server unresponsive
  • -W 交互要求输入密码
  • -H 指定主机:端口
  • -D cn - 指示ldapsearch专门绑定到这个地址(cn) - 据我了解,这相当于用这个CN登录LDAP,只要给下面提示的密码就可以了
  • -b ... 指定将过滤结果的基本 DN
  • 不要忘记上面单引号中的最后一部分(单引号或双引号,我认为这没有任何区别)非常重要,因为这指定了 ldapsearch 在绑定/登录后将执行的查询 - 如果您没有指定任何内容,它将尝试执行所有条目的搜索,并可能导致 LDAP 服务器无响应

and finally, {0} in Spring Boot security config is where "someuser" from ldapsearch goes, i.e. Spring will construct the address to bind to from what is given in userDnPatterns, replacing {0} with whatever username is specified during login

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

PS. this worked with an older version of Oracle Internet Directory, where all users we wanted to authenticate were under cn=Users instead of e.g. ou=people in the corresponding Spring Boot LDAP tutorial (i guess)

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