通过'userPrincipalName'搜索Active Directory的Spring Security - 有没有办法改变这个?

时间:2014-06-25 21:32:04

标签: java spring spring-mvc spring-security active-directory

我还在学习LDAP / Active Directory,所以如果我的术语完全错误,请纠正我:)

在我们的Java Web应用程序中,我正在尝试使用Spring Security LDAP来保护它。我设法让Spring Security使用内存中的身份验证,但我们需要将它绑定到我们的AD服务器上。

我将使用 com.test

屏蔽我们的实际域名

以下是我尝试从我的应用程序登录时收到的错误

  

13:39:55,701 ERROR ActiveDirectoryLdapAuthenticationProvider:133 -   无法找到经过身份验证的用户的目录条目:johnsmit   javax.naming.NameNotFoundException:[LDAP:错误代码32 - 0000208D:   NameErr:DSID-0310020A,问题2001(NO_OBJECT),数据0,最佳匹配   of:'CN = Users,DC = ad,DC = test,DC = com'

我在Spring中使用基于类的配置 这是我的SecurityConfiguration类

@Configuration
@EnableWebMvcSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
          provider = new ActiveDirectoryLdapAuthenticationProvider("ad.test.com", "ldap://servername.ad.test.com:389/cn=Users,dc=ad,dc=test,dc=com");

        provider.setUseAuthenticationRequestCredentials(true);


        return provider;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
                authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().failureUrl("/login?error")
                .loginPage("/login")
                .permitAll()
                .and()
                .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login")
                .permitAll()
                .and()
                .httpBasic();

    }
}

所以这是问题(至少我认为)...... 在我们的AD服务器中,我们有 cn 名称 sAMAccountName uid 作为我们登录的用户名, johnsmit 在上面的例子中。

我们的userPrincipalName(在我们的AD服务器中)是我们的电子邮件地址,所以 john.smith@test.com

我正在查看ActiveDirectoryLdapAuthenticationProvider类,它说它使用userPrincipalName。查看代码here on github,它表明它正在使用userPrincipalName。我检查了Spring Security的新版本,它不是一般可用性,但它是一样的。

如果是searchFilter String searchFilter = "(&(objectClass=user)(sAMAccountName={0}))";这将是理想的情况,但我不知道是否可以覆盖任何地方,我找不到任何文件?

3 个答案:

答案 0 :(得分:1)

我想我会用这张Jira票来回答我自己的问题

https://jira.spring.io/browse/SEC-1915

看起来它还没有被合并。这张票中的补丁将是我需要的答案。

答案 1 :(得分:1)

当我发现这对解决自己的问题非常有帮助时,我想补充一下,@JanTheGun建议的更改似乎已合并到Spring Security 5.0.8.RELEASE / Spring Boot 2.0中。 5

ActiveDirectoryLdapAuthenticationProvider.setSearchFilter(String)的JavaDoc读取:

  

用于搜索正在认证的用户的LDAP过滤器字符串。 {0}的出现被username@domain取代。 {1}的出现仅被替换为用户名。

     

默认为:(&(objectClass=user)(userPrincipalName={0})))

因此,在Spring Security 5.0.8.RELEASE中,可以使用建议的搜索过滤器更改,而无需复制任何Spring Security类!

答案 2 :(得分:0)

三年后,我也在努力解决这个问题。由于建议将userPrincipalName更改为Office365的邮件地址(请参阅Office 365 – Why Your UPN Should Match Your Primary SMTP Address),我认为其他人可能也有问题 - 所以这是我的修复。

关于github的讨论正在讨论这个问题: https://github.com/spring-projects/spring-security/issues/2448

用户gkibilov的答案解决了我的问题。我们的想法是改变" searchForUser" ActiveDirectoryLdapAuthenticationProvider类中的方法,不仅要传递bindprincipal(= USERNAME @ DOMAIN),还要传递用户名。

之后,可以使用以下searchFilter来查找sAMAccountName而不是userPrincipalName:

"(&(objectClass=user)(sAMAccountName={1}))"

以下是我执行的单个步骤:

  1. 复制 ActiveDirectoryAuthenticationException 类的源代码 到一个新类并重命名它(即" MyActiveDirectoryAuthenticationException "):SourceLink

  2. 复制 ActiveDirectoryLdapAuthenticationProvider 的来源 类到一个新类并重命名它(即 " MyActiveDirectoryLdapAuthenticationProvider "):SourceLink

  3. 使用新的异常类( MyActiveDirectoryAuthenticationException )交换 MyActiveDirectoryLdapAuthenticationProvider 中的 ActiveDirectoryAuthenticationException

  4. 更改 MyActiveDirectoryAuthenticationException 类中的方法以传递用户名:

    private DirContextOperations searchForUser(DirContext context, String username)
        throws NamingException {
         SearchControls searchControls = new SearchControls();
         searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    
         String bindPrincipal = createBindPrincipal(username);
         String searchRoot = rootDn != null ? rootDn
                 : searchRootFromPrincipal(bindPrincipal);
    
         try {
             return SpringSecurityLdapTemplate.searchForSingleEntryInternal(context,
                searchControls, searchRoot, searchFilter,
                new Object[] { bindPrincipal, username});
         }
         ...
    }
    
  5. 更改searchFilter以查找sAMAccountName属性。在你的情况下,bean应该是这样的:

    @Bean
    public MyActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
        provider = new MyActiveDirectoryLdapAuthenticationProvider("ad.test.com", "ldap://servername.ad.test.com:389/cn=Users,dc=ad,dc=test,dc=com");
        provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={1}))");
        provider.setUseAuthenticationRequestCredentials(true);
        return provider;
    }
    
  6. 我不得不承认,复制课程并不是最好的解决方案,但原来的课程是#34; final"我无法找到解决此问题的更好方法。