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

标签 java authentication spring-boot spring-security ldap

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

我想使用用户在登录表单中输入的用户名和密码设置 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 但我不得不再次检查硬编码的用户名和密码 :(

最佳答案

我终于让它工作了.. :) 我找到了我想要的 here . (Ali Miskeen 的回答)

我尝试使用 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);

    }
}

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

关于java - 登录表单用户凭据而不是 LDAP Spring Security 中的硬编码 UserDn 和密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49622927/

相关文章:

java - 用java编码折线算法

spring - Rest Controller 如何同时处理单个实例应用程序的多个请求?

javascript - 将应用程序部署到 Heroku,在后端 Java Spring Boot 中抛出 500 内部服务器错误,同时在本地环境中完美运行

java - Spring Boot JPA(Hibernate)如何保存Images

Java 无效的十六进制字符 : h

java - Jenkins 会吞下 MojoFailureException 吗?

java - 谷歌服务器验证码导致登录失败

android - 使用 Ksoap 登录 Android

java - j_security_check 在 weblogic 中如何工作?

java - 选择特定的 selectOneMenu 选项时如何显示 inputText?