登录表单用户凭据而不是 LDAP Spring Security 中的硬编码 UserDn 和密码

Posted

技术标签:

【中文标题】登录表单用户凭据而不是 LDAP Spring Security 中的硬编码 UserDn 和密码【英文标题】:Login form user credentials instead of hard-coded UserDn and Password in LDAP Spring Security 【发布时间】:2018-09-12 08:35:36 【问题描述】:

我已经使用 Spring Boot 通过 LDAP 实现了 spring 安全性。我能够成功地与我的公司 LDAP 服务器绑定,但使用硬编码的值。这是我可以与我的公司 LDAP 服务器绑定并继续进行的唯一方法,因为我不知道要成功绑定的管理员/通用 UserDN 或密码。由于某些机密原因,该公司没有向我提供管理员凭据。

我想使用用户在登录表单中输入的用户名和密码设置 ContextSource 的 UserDn 和密码。但是这里的问题是在启动 Tomcat 服务器时扫描了 SecurityConfig 类,而在登录过程之后,控制权根本没有到达 SecurityConfig 类。我怎么解决这个问题?需要一些帮助。

这是我的 SecurityConfig 类:

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private CustomAuthenticationFailureHandler customAuthFailureHandler;

    @Autowired
    private CustomAuthenticationSuccessHandler customAuthSuccessHandler;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 

        httpSecurity
            .authorizeRequests()
                .anyRequest().fullyAuthenticated()
                .and()
            .formLogin()
                .loginPage("/login").permitAll()
                .loginProcessingUrl("/sign-in")
                .usernameParameter("userid")
                .passwordParameter("password")
                .successHandler(customAuthSuccessHandler)
                .failureHandler(customAuthFailureHandler)
                .permitAll()
                .and()
            .logout()
                .logoutSuccessUrl("/logout")
                .permitAll()
                .and()
            .csrf().disable();

    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 

        auth.authenticationProvider(ldapAuthProvider());

    

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Bean
    public AuthenticationProvider ldapAuthProvider() throws Exception 

        DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldaps://some.domain.com:3269/");
        contextSource.setUserDn("username@domain.com"); // Here I want to set the username from Login screen
        contextSource.setPassword("password"); // also password from login screen
        contextSource.afterPropertiesSet();
        String userSearchFilter = "(sAMAccountName=username)"; // Here too I need to set username from login screen
        LdapUserSearch ldapUserSearch = new FilterBasedLdapUserSearch("dc=domain,dc=com", userSearchFilter, contextSource);
        BindAuthenticator bindAuth = new BindAuthenticator(contextSource);
        bindAuth.setUserSearch(ldapUserSearch);
        LdapAuthenticationProvider ldapAuthProvider = new LdapAuthenticationProvider(bindAuth);

        return ldapAuthProvider;
    

我创建了一个 AuthenticationProvider bean 方法,并在 AuthenticationManagerBuilder 中设置它。我也尝试创建一个 CustomAuthenticationProvider 但我不得不再次检查硬编码的用户名和密码:(

【问题讨论】:

嗨,我觉得这应该对@SpringBootApplication(exclude = SecurityAutoConfiguration.class )有帮助 嗨@Jayesh,添加这个会禁用Spring Security,对吗?这是否会达到不在 SecurityConfig 类中设置硬编码值的目的?我是 SpringBoot 的新手,因此对此有点怀疑。 无论如何我尝试排除 SecurityAutoConfig 并且在启动 tomcat 时出现异常。它说 SecurityConfig Bean 无法初始化..有关 ObjectPostProcessor bean 的一些依赖错误 您是否为安全创建了自己的 bean?我的代码 sn-p 只会阻止自动配置为您创建默认配置。 不,我没有,我正在使用 spring security 并尝试使用 LDAP 来允许同事使用他们公司的 LDAP 凭据登录到应用程序 【参考方案1】:

我终于让它工作了.. :) 我找到了我想要的东西 here 。 (阿里·米斯基恩的回答)

我尝试使用 CustomAuthenticationProvider 方法本身,但这次我使用传统的 JNDI LDAP 方法检查身份验证。我还想检查 3 个不同的 LDAP 服务器,因此这种方法最适合我的应用程序。

这是我完整的 CustomAuthenticationProvider 实现:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider 

    private static final String AT_DOMAIN_COM = "@domain.com";

    private static final String SINGLE_SPACE = " ";

    @Value("$ldap.url.for.server1")
    private String ldapUrlForServer1; // url set in application.properties

    @Value("$ldap.url.for.server2")
    private String ldapUrlForServer2;

    @Value("$ldap.url.for.server3")
    private String ldapUrlForServer3;

    @Value("$ldap.jndi.context.factory")
    private String ldapContextFactory;

    @Value("$ldap.authentication.type")
    private String ldapAuthenticationType; // auth type is "simple"

    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException 

        String username = auth.getName();
        String password = auth.getCredentials().toString();

        if (isLdapRegisteredUser(username, password)) 
            // use the credentials and authenticate against a third-party system
            return new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>());
         else 
            return null;
        

    

    boolean isLdapRegisteredUser(String username, String password) 

        boolean result = false;
        Hashtable<String, String> env = new Hashtable<>();
        LdapContext ctx = null;

        try 

            env.put(Context.INITIAL_CONTEXT_FACTORY, ldapContextFactory);
            env.put(Context.SECURITY_AUTHENTICATION, ldapAuthenticationType);
            env.put(Context.SECURITY_PRINCIPAL, username + AT_DOMAIN_COM);
            env.put(Context.SECURITY_CREDENTIALS, password);
            // here I'm checking for 3 different LDAP servers
            env.put(Context.PROVIDER_URL,  ldapUrlForServer1 + SINGLE_SPACE + ldapUrlForServer2 + SINGLE_SPACE + ldapUrlForServer3); 

            ctx = new InitialLdapContext(env, null);

            if (ctx != null) 
                result = true;
                String selectedLdapUrl = ctx.getEnvironment().get(Context.PROVIDER_URL).toString();
                // do further operations with "ctx" if needed
                System.out.println("selected LDAP url is: " + selectedLdapUrl);
                System.out.println("Connection Successful!");
            

         catch(NamingException nex) 
            nex.printStackTrace();
         finally 
            if (ctx != null) 
                try 
                    ctx.close();
                 catch (Exception ex) 
                
            
        

        return result;

    


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


这是我的 SecurityConfig 实现:

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private CustomAuthenticationProvider customAuthProvider;

    @Autowired
    private CustomAuthenticationFailureHandler customAuthFailureHandler;

    @Autowired
    private CustomAuthenticationSuccessHandler customAuthSuccessHandler;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception 

        httpSecurity
            .authorizeRequests()
                .antMatchers("/css/**").permitAll()
                .antMatchers("/fonts/**").permitAll()
                .antMatchers("/images/**").permitAll()
                .antMatchers("/js/**").permitAll()
                .anyRequest().fullyAuthenticated()
                .and()
            .formLogin()
                .loginPage("/login").permitAll()
                .loginProcessingUrl("/sign-in")
                .usernameParameter("userid")
                .passwordParameter("password")
                .successHandler(customAuthSuccessHandler)
                .failureHandler(customAuthFailureHandler)
                .permitAll()
                .and()
            .logout()
                .clearAuthentication(true)
                .logoutSuccessUrl("/login").permitAll()
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .and()
            .csrf().disable();

    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 

        auth.authenticationProvider(customAuthProvider);

    

希望这会有所帮助 :) 编码愉快...

【讨论】:

以上是关于登录表单用户凭据而不是 LDAP Spring Security 中的硬编码 UserDn 和密码的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security LDAP 登录错误凭据

无法使用具有正确凭据的 Spring 3.1 验证 LDAP 帐户

带有表单登录的 Spring Security OAuth 2

Spring Security Ldap验证userDn和登录表单中的密码

Spring Security 自定义登录回退

Ldap Auth 作为 Rest 控制器