2013-11-25 3 views
0

Я пытаюсь добавить вторичный источник контекста ldap для весенней безопасности в среде с разделенным доменом, и, похоже, я немного отстаю. Я понимаю, что подобные вопросы заданы раньше, но это для двух отдельных доменов, регистрирующихся в одном приложении.Добавление нескольких источников ldap в Spring-security в многодоменной среде

Мой первый шаг должен был добавить дополнительный источник контекста в мой файл XML безопасности конфигурации следующим образом:

<beans:bean id="secondaryContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> 
    <beans:constructor-arg value="ldap://<ldap address>:389/DC=example,DC=com"/> 
    <beans:property name="userDn" value="CN=BindAccount,CN=Users,DC=example,DC=com" /> 
    <beans:property name="password" value="examplepw" /> 
</beans:bean> 

Кроме того, я добавил конструктор-Arg к ldapAuthenticationProvider и заменил BindAuthenticator с моим пользовательский класс как так:

<beans:bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">  
    <beans:constructor-arg> 
     <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> 
      <beans:constructor-arg ref="contextSource" /> 
      <beans:constructor-arg ref="secondaryContextSource" /> 
      <beans:property name="userSearch"> 
       <beans:bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> 
        <beans:constructor-arg index="0" value="CN=Users"/> 
        <beans:constructor-arg index="1" value="(userPrincipalName={0})"/> 
        <beans:constructor-arg index="2" ref="contextSource" /> 
       </beans:bean> 
      </beans:property> 

     </beans:bean> 
    </beans:constructor-arg>  
    <beans:property name="userDetailsContextMapper"> 
     <beans:bean id="employeeServiceFacade" class="com.example.service.security.EmployeeServiceFacade" /> 
    </beans:property> 
     <beans:constructor-arg> 
     <beans:bean class="com.example.web.security.CustomLdapAuthoritiesPopulator" /> 
    </beans:constructor-arg> 
</beans:bean> 

Затем я попытался расширить BindAuthenticator принять и установить дополнительный источник контекста в конструкторе. Первоначально я не мог заставить это работать, поэтому полностью переписал класс BindAuthenticator и расширил AbstractLdapAuthenticator, чтобы обойти BindAuthenticator. Затем я перепробовал метод аутентификации, чтобы проверить, содержит ли имя пользователя вторичное DN, и если бы это произошло, я бы снова вызвал bindWithDn, чтобы попытаться переподтвердить вторичный домен. Вот где я думаю, что все это неправильно, потому что, когда он пытается получить новую Dn, она терпит неудачу. В основном он утверждает, что он не может связываться с доменом. (Я тройная проверила настройки домена и подключила к нему с консолью администрирования LDAP и взяла эти настройки для моего приложения) Вот моя расширенная BindAuthenticator

public class ExtendedBindAuthenticator extends AbstractLdapAuthenticator { 

    private BaseLdapPathContextSource secondaryContextSource; 

    public void setSecondContextSource(BaseLdapPathContextSource secondContextSource) { 
     this.secondaryContextSource = secondaryContextSource; 
    } 


    public ExtendedBindAuthenticator(BaseLdapPathContextSource contextSource, BaseLdapPathContextSource secondContextSource) { 
     super(contextSource); 
     this.secondaryContextSource = secondaryContextSource; 
    } 


    public DirContextOperations authenticate(Authentication authentication) { 
     DirContextOperations user = null; 
     Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, 
       "Can only process UsernamePasswordAuthenticationToken objects"); 

     String username = authentication.getName(); 
     String password = (String)authentication.getCredentials();   

     if(username.contains("secondDomain")) { 
      DirContextOperations secondaryUser = getUserSearch().searchForUser(username); 
      this.bindWithDn(secondaryUser.getDn().toString(), username, password); 

     } 


     if (!StringUtils.hasLength(password)) { 

      throw new BadCredentialsException(messages.getMessage("BindAuthenticator.emptyPassword", 
        "Empty Password")); 
     } 

     // If DN patterns are configured, try authenticating with them directly 
     for (String dn : getUserDns(username)) { 
      user = this.bindWithDn(dn, username, password); 

      if (user != null) { 
       break; 
      } 
     } 

     // Otherwise use the configured search object to find the user and authenticate with the returned DN. 
     if (user == null && getUserSearch() != null) { 
      DirContextOperations userFromSearch = getUserSearch().searchForUser(username); 
      user = bindWithDn(userFromSearch.getDn().toString(), username, password); 
     } 

     if (user == null) { 
      throw new BadCredentialsException(
        messages.getMessage("BindAuthenticator.badCredentials", "Bad credentials")); 
     } 

     return user; 
    } 

    private DirContextOperations bindWithDn(String userDnStr, String username, String password) { 
     BaseLdapPathContextSource ctxSource = (BaseLdapPathContextSource) getContextSource(); 

     if(username.contains("secondDomain")) { 
      ctxSource = secondaryContextSource; 
     } 

     DistinguishedName userDn = new DistinguishedName(userDnStr); 
     DistinguishedName fullDn = new DistinguishedName(userDn); 
     fullDn.prepend(ctxSource.getBaseLdapPath()); 


     DirContext ctx = null; 
     try { 
      ctx = getContextSource().getContext(fullDn.toString(), password); 
      // Check for password policy control 
      PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor.extractControl(ctx); 



      Attributes attrs = ctx.getAttributes(userDn, getUserAttributes()); 

      DirContextAdapter result = new DirContextAdapter(attrs, userDn, ctxSource.getBaseLdapPath()); 

      if (ppolicy != null) { 
       result.setAttributeValue(ppolicy.getID(), ppolicy); 
      } 

      return result; 
     } catch (NamingException e) { 
      // This will be thrown if an invalid user name is used and the method may 
      // be called multiple times to try different names, so we trap the exception 
      // unless a subclass wishes to implement more specialized behaviour. 
      if ((e instanceof org.springframework.ldap.AuthenticationException) 
        || (e instanceof org.springframework.ldap.OperationNotSupportedException)) { 
       handleBindException(userDnStr, username, e); 
      } else { 
       throw e; 
      } 
     } catch (javax.naming.NamingException e) { 
      throw LdapUtils.convertLdapException(e); 
     } finally { 
      LdapUtils.closeContext(ctx); 
     } 

     return null; 
    } 

    /** 
    * Allows subclasses to inspect the exception thrown by an attempt to bind with a particular DN. 
    * The default implementation just reports the failure to the debug logger. 
    */ 
    protected void handleBindException(String userDn, String username, Throwable cause) { 
     System.out.println("Failed to bind as " + userDn + ": " + cause); 
    } 

}

Если кто имеет опыт работы с этим я бы очень благодарен за это, поскольку я не мог найти много на эту тему. Я надеялся, что кто-то скажет мне, если я на правильном пути, или если я буду заниматься этим по-другому. Чтобы быть ясным, я использую spring-security-ldap, а не spring-ldap. Также просто хочу упомянуть, что у меня есть все мои зависимости в моем файле pom. Благодаря!

ответ

1

Непонятно из вашего вопроса, что на самом деле происходит не так - например, конфигурация, которую вы на самом деле не загружаете, поскольку она использует Spring Security BindAuthenticator и пытается передать ей два аргумента ContextSource.

Если бы я был вами, я бы не пытался взломать внутренние классы реализации и вместо этого оставил их в покое и написал отдельный класс делегирования на основе ваших критериев выбора.

Сначала я бы определил два отдельных фаната LdapAuthenticationProvider, по одному для каждого домена, и сначала убедитесь, что вы можете аутентифицировать пользователей с каждым из них, вызвав их непосредственно в модульном тесте. Убедитесь, что вы правильно настроили каждый из них для своих доменов, прежде чем пытаться использовать их вместе.

После этого я проведу их в отдельное делегирование AuthenticationProvider. Кое-что вроде:

public class DelegatingLdapAuthenticationProvider implements AuthenticationProvider { 
    // Inject these via the app context 
    private LdapAuthenticationProvider primary; 
    private LdapAuthenticationProvider secondary; 

    public Authentication authenticate(Authentication a) { 
     if (a.getName().contains("secondDomain")) { 
      return secondary.authenticate(a); 
     } else { 
      return primary.authenticate(a); 
     } 
    } 
} 

Я бы сконфигурировал этот компонент в качестве провайдера аутентификации, который фактически называет Spring Security.

+0

Извините за смутного вопрос, но Я переработал свое приложение так, как вы предлагали, и теперь он принимает пользователей для двух доменов. Спасибо за вашу помощь. – Dan

+0

Добро пожаловать. Рад, что вы его работали. –

+0

@ Dan Возможно ли вам показать конфигурацию xml конфигурации Spring security? Я пытаюсь разработать аналогичное решение, но с помощью Active Directory. – Ondreju

0

Я использовал ниже реализации для того же, в этом случае учетные данные не найдены в один Ldap/объявление, оно будет проверять в другой Ldap/объявление, и реагировать соответствующим образом:

@Configuration 
@EnableWebSecurity 
@EnableConfigurationProperties 
@EnableGlobalMethodSecurity(prePostEnabled = true) 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

@Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
      configureLdapP(auth); 
      configureActiveDirectoryP(auth); 
      configureLdap(auth); 
      configureActiveDirectory(auth); 
     } 

    @Bean 
    protected LdapLoginRequestFilter ldapLoginRequestFilter() throws Exception { 
     return new LdapLoginRequestFilter("/login/ldap", authenticationManager(), authenticationResultHandler); 
    } 


    private void configureActiveDirectory(AuthenticationManagerBuilder auth) { 
     ActiveDirectoryLdapAuthenticationProvider adProvider = activeDirectoryLdapAuthenticationProvider(); 
     if(adProvider != null) auth.authenticationProvider(adProvider); 
    } 

    private void configureActiveDirectoryP(AuthenticationManagerBuilder auth) { 
     ActiveDirectoryLdapAuthenticationProvider adProvider = activeDirectoryLdapAuthenticationProviderP(); 
     if(adProvider != null) auth.authenticationProvider(adProvider); 
    } 

    private void configureLdap(AuthenticationManagerBuilder auth) throws Exception { 
     String ldapServerUrl = "ldap url 1"; 
     String ldapUserDnPattern = "ldap user dn pattern"; 
     if (StringUtils.isNotBlank(ldapServerUrl) && StringUtils.isNotBlank(ldapUserDnPattern)) { 
      auth.ldapAuthentication() 
      .userDnPatterns(ldapUserDnPattern) 
      .contextSource().url(ldapServerUrl); 
     } 
    } 

    private void configureLdapP(AuthenticationManagerBuilder auth) throws Exception { 
     String ldapServerUrl = "ldap url 2"; 
     String ldapUserDnPattern = "ldap user dn pattern"; 
     if (StringUtils.isNotBlank(ldapServerUrl) && StringUtils.isNotBlank(ldapUserDnPattern)) { 
      auth.ldapAuthentication() 
      .userDnPatterns(ldapUserDnPattern) 
      .contextSource().url(ldapServerUrl); 
     } 
    } 
    @Bean 
    protected ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() { 


     ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain 2", "ad url 2", 
       null); 
     provider.setConvertSubErrorCodesToExceptions(true); 
     provider.setUseAuthenticationRequestCredentials(true); 
     provider.setUserDetailsContextMapper(new CustomUserDetailsContextMapper()); 
     return provider; 
    } 

    @Bean 
    protected ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProviderP() { 

     ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("domain1", "ad url 1", 
       null); 
     provider.setConvertSubErrorCodesToExceptions(true); 
     provider.setUseAuthenticationRequestCredentials(true); 
     provider.setUserDetailsContextMapper(new CustomUserDetailsContextMapper()); 
     return provider; 
    } 
} 
Смежные вопросы