![](/img/trans.png)
[英]Login form user credentials instead of hard-coded UserDn and Password in LDAP Spring Security
[英]Spring Security Ldap authentication userDn and password from login form
我正在嘗試使用WebSecurityConfigurerAdapter實現 Spring Security LDAP 身份驗證。
到目前為止它工作正常,但在我的情況下的問題是我不希望上下文的用戶名和密碼被硬編碼。 它必須是用戶的登錄名和密碼,所以我的問題是如何從登錄表單構建用戶名和密碼的上下文和設置?
這是我正在使用的代碼:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userSearchFilter("(sAMAccountName={0})")
.contextSource(contextSource());
}
@Bean
public BaseLdapPathContextSource contextSource() {
LdapContextSource bean = new LdapContextSource();
bean.setUrl("ldap://10.10.10.10:389");
bean.setBase("DC=myDomaine,DC=com");
//instead of this i want to put here the username and password provided by the user
bean.setUserDn("myDomaine\\username");
bean.setPassword("password");
bean.setPooled(true);
bean.setReferral("follow");
bean.afterPropertiesSet();
return bean;
}
}
謝謝!
您的代碼應該可以正常工作。 硬編碼的用戶名和密碼僅用於創建與 ldap 服務器的綁定。 登錄表單中提供的用戶名和密碼僅使用您的代碼進行身份驗證。
我使用以下代碼來執行 ldap 身份驗證。
public void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userSearchFilter("sAMAccountName={0}").contextSource().url(this.ldapUrl).managerDn(this.managerDn)
.managerPassword(this.managerPassword);
}
其中 manager 是用於創建與服務器綁定的 ldap 帳戶。
contextSource 中的 userDN 和 password 參數是必需參數。 就像管理員用戶名和密碼一樣,您可以獲取或創建到 ldap 服務器的初始連接。
讓您能夠從登錄表單驗證用戶名和密碼。 您可以使用 ldapTemplate:
@Bean
public BaseLdapPathContextSource contextSource() {
LdapContextSource bean = new LdapContextSource();
bean.setUrl("ldap://10.10.10.10:389");
bean.setBase("DC=myDomaine,DC=com");
//instead of this i want to put here the username and password provided by the user
bean.setUserDn("myDomaine\\username");
bean.setPassword("password");
bean.setPooled(true);
bean.setReferral("follow");
bean.afterPropertiesSet();
return bean;
}
@Bean
public LdapTemplate ldapTemplate() {
LdapTemplate template = new LdapTemplate(contextSource());
return template;
}
然后在您的服務類實現中使用它:
@Service
public class LdapUserServiceImpl implements LdapUserService, BaseLdapNameAware {
@Autowired
protected ContextSource contextSource;
@Autowired
protected LdapTemplate ldapTemplate;
@Override
public boolean authenticate(String userDn, String credentials) {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("sAMAccountName", userDn));
return ldapTemplate.authenticate("", filter.toString(), credentials);
}
}
然后調用此服務,從登錄表單傳遞用戶名和密碼,如下所示:
boolean isAuthenticated = ldapUserService.authenticate(loginForm.getUsername(), loginForm.getPassword());
您可以使用自定義的身份驗證提供程序進行身份驗證。
在您的安全配置中,您可以自動連接 CustomAuthenticationProvider,它可以從登錄表單中獲取用戶名和密碼:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider customAuthProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin();
}
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthProvider);
}
}
現在只需實現 CustomAuthenticationProvider
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOG = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
@Value("${ldap.host}")
String ldapHost;
@Value("${ldap.port}")
String ldapPort;
@Value("${ldap.base-dn}")
String baseDomainName;
@Value("${ldap.domain-prefix}")
String domainPrefix;
@Value("${ldap.read.timeout}")
String timeout;
private static final String DEFAULT_JNDI_CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
//user and password are from user's input from login form...
String user= authentication.getName();
String password = authentication.getCredentials().toString();
try {
Set<String> roles = authenticate(user, password);
if (CollectionUtils.isEmpty(roles))
return null;
List<GrantedAuthority> authorityList = new ArrayList<>();
for(String role: roles){
authorityList.add(new SimpleGrantedAuthority(role));
}
return new UsernamePasswordAuthenticationToken(user, password, authorityList);
} catch (NamingException ex) {
LOG.info("Naming Exception",ex);
}
return null;
}
public Set<String> authenticate(final String username, final String password) throws NamingException {
InitialLdapContext ctx = null;
NamingEnumeration<SearchResult> results = null;
try {
final Hashtable<String, String> ldapEnvironment = new Hashtable<>();
ldapEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, DEFAULT_JNDI_CONTEXT_FACTORY);
ldapEnvironment.put(Context.PROVIDER_URL, "ldap://" + ldapHost + ":" + ldapPort + "/" + baseDomainName);
ldapEnvironment.put(Context.SECURITY_PROTOCOL, "ssl");
ldapEnvironment.put(Context.SECURITY_CREDENTIALS, password);
ldapEnvironment.put(Context.SECURITY_PRINCIPAL, domainPrefix + '\\' + username);
ldapEnvironment.put("com.sun.jndi.ldap.read.timeout", timeout);
ldapEnvironment.put("com.sun.jndi.ldap.connect.timeout", timeout);
ctx = new InitialLdapContext(ldapEnvironment, null);
final SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[]{"memberOf"});
constraints.setReturningObjFlag(true);
results = ctx.search("", "(sAMAccountName=" + username + ")", constraints);
if (!results.hasMore()) {
LOG.warn(".authenticate(" + ldapHost + "," + username + "): unable to locate " + username);
return null;
}
final Set<String> adGroups = new TreeSet<>();
final SearchResult entry = results.next();
for (NamingEnumeration valEnum = entry.getAttributes().get("memberOf").getAll(); valEnum.hasMoreElements(); ) {
String dn = (String) valEnum.nextElement();
int i = dn.indexOf(",");
if (i != -1) {
dn = dn.substring(0, i);
}
if (dn.startsWith("CN=")) {
dn = dn.substring("CN=".length());
}
adGroups.add(dn);
}
return adGroups;
}
finally {
try {
if (null != results)
results.close();
}
catch (Throwable ignored) {
}
try {
if (null != ctx)
ctx.close();
}
catch (Throwable ignored) {
}
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(
UsernamePasswordAuthenticationToken.class);
}
}
通過不重寫自己的 LdapAuthenticationProvider 來節省自己的時間,現有的 ActiveDirectoryLdapAuthenticationProvider 將使用收到的憑據進行 LDAP 身份驗證,如果您想做更多,您還可以添加 searchFilter(例如,查看用戶是否也屬於特定組)
相關文檔:
示例片段:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private LdapProperties ldapProperties;
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider authenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider(ldapProperties.getDomain(), ldapProperties.getProviderUrl());
authenticationProvider.setConvertSubErrorCodesToExceptions(true);
authenticationProvider.setUseAuthenticationRequestCredentials(true);
//if you're not happy on the default searchFilter, you can set your own. See https://docs.spring.io/spring-security/site/docs/4.2.18.RELEASE/apidocs/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.html#setSearchFilter-java.lang.String-
authenticationProvider.setSearchFilter("(&(objectClass=user)(cn={1}))");
return authenticationProvider;
}
...
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.