我已经使用 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/