2008-09-17 5 views
27

Я пишу веб-приложение Spring, которое требует от пользователей входа в систему. У моей компании есть сервер Active Directory, который я бы хотел использовать для этой цели. Однако у меня возникли проблемы с использованием Spring Security для подключения к серверу.Как выполнить проверку подлинности на сервере Active Directory с использованием Spring Security?

Я использую Spring 2.5.5 и Spring Security 2.0.3 вместе с Java 1.6.

Если я изменяю URL-адрес LDAP на неправильный IP-адрес, он не генерирует исключение или что-то еще, поэтому мне интересно, даже если пытается подключиться к серверу для начала.

Хотя веб-приложение запускается просто отлично, любая информация, вводимая на страницу входа, отклоняется. Раньше я использовал InMemoryDaoImpl, который работал отлично, поэтому остальная часть моего приложения, похоже, правильно настроена.

Вот мои связанные с безопасностью зерна:

<beans:bean id="ldapAuthProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider"> 
    <beans:constructor-arg> 
     <beans:bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> 
     <beans:constructor-arg ref="initialDirContextFactory" /> 
     <beans:property name="userDnPatterns"> 
      <beans:list> 
      <beans:value>CN={0},OU=SBSUsers,OU=Users,OU=MyBusiness,DC=Acme,DC=com</beans:value> 
      </beans:list> 
     </beans:property> 
     </beans:bean> 
    </beans:constructor-arg> 
    </beans:bean> 

    <beans:bean id="userDetailsService" class="org.springframework.security.userdetails.ldap.LdapUserDetailsManager"> 
    <beans:constructor-arg ref="initialDirContextFactory" /> 
    </beans:bean> 

    <beans:bean id="initialDirContextFactory" class="org.springframework.security.ldap.DefaultInitialDirContextFactory"> 
    <beans:constructor-arg value="ldap://192.168.123.456:389/DC=Acme,DC=com" /> 
    </beans:bean> 
+0

На самом деле это не так ответ, как уточняющий вопрос - у вас уже завершена регистрация весенних пакетов безопасности? – 2008-09-17 17:52:40

+0

У меня включена регистрация на все. Не вижу никаких сообщений, которые регистрируются ... Я обновил свой вопрос с помощью моей конфигурации Log4J. – Michael 2008-09-17 18:35:26

+0

Скажите мне файлы флага, необходимые для этого. please – addy 2013-09-06 10:28:14

ответ

35

У меня был такой же опыт, Стук-моя-голова-против-стены вы сделали, и в конечном итоге написание пользовательского поставщика проверки подлинности, который делает LDAP запрос к серверу Active Directory.

Так мои бобы, связанные с безопасностью, являются:

<beans:bean id="contextSource" 
    class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> 
    <beans:constructor-arg value="ldap://hostname.queso.com:389/" /> 
</beans:bean> 

<beans:bean id="ldapAuthenticationProvider" 
    class="org.queso.ad.service.authentication.LdapAuthenticationProvider"> 
    <beans:property name="authenticator" ref="ldapAuthenticator" /> 
    <custom-authentication-provider /> 
</beans:bean> 

<beans:bean id="ldapAuthenticator" 
    class="org.queso.ad.service.authentication.LdapAuthenticatorImpl"> 
    <beans:property name="contextFactory" ref="contextSource" /> 
    <beans:property name="principalPrefix" value="QUESO\" /> 
</beans:bean> 

Тогда класс LdapAuthenticationProvider:

/** 
* Custom Spring Security authentication provider which tries to bind to an LDAP server with 
* the passed-in credentials; of note, when used with the custom {@link LdapAuthenticatorImpl}, 
* does <strong>not</strong> require an LDAP username and password for initial binding. 
* 
* @author Jason 
*/ 
public class LdapAuthenticationProvider implements AuthenticationProvider { 

    private LdapAuthenticator authenticator; 

    public Authentication authenticate(Authentication auth) throws AuthenticationException { 

     // Authenticate, using the passed-in credentials. 
     DirContextOperations authAdapter = authenticator.authenticate(auth); 

     // Creating an LdapAuthenticationToken (rather than using the existing Authentication 
     // object) allows us to add the already-created LDAP context for our app to use later. 
     LdapAuthenticationToken ldapAuth = new LdapAuthenticationToken(auth, "ROLE_USER"); 
     InitialLdapContext ldapContext = (InitialLdapContext) authAdapter 
       .getObjectAttribute("ldapContext"); 
     if (ldapContext != null) { 
      ldapAuth.setContext(ldapContext); 
     } 

     return ldapAuth; 
    } 

    public boolean supports(Class clazz) { 
     return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz)); 
    } 

    public LdapAuthenticator getAuthenticator() { 
     return authenticator; 
    } 

    public void setAuthenticator(LdapAuthenticator authenticator) { 
     this.authenticator = authenticator; 
    } 

} 

Тогда класс LdapAuthenticatorImpl:

/** 
* Custom Spring Security LDAP authenticator which tries to bind to an LDAP server using the 
* passed-in credentials; does <strong>not</strong> require "master" credentials for an 
* initial bind prior to searching for the passed-in username. 
* 
* @author Jason 
*/ 
public class LdapAuthenticatorImpl implements LdapAuthenticator { 

    private DefaultSpringSecurityContextSource contextFactory; 
    private String principalPrefix = ""; 

    public DirContextOperations authenticate(Authentication authentication) { 

     // Grab the username and password out of the authentication object. 
     String principal = principalPrefix + authentication.getName(); 
     String password = ""; 
     if (authentication.getCredentials() != null) { 
      password = authentication.getCredentials().toString(); 
     } 

     // If we have a valid username and password, try to authenticate. 
     if (!("".equals(principal.trim())) && !("".equals(password.trim()))) { 
      InitialLdapContext ldapContext = (InitialLdapContext) contextFactory 
        .getReadWriteContext(principal, password); 

      // We need to pass the context back out, so that the auth provider can add it to the 
      // Authentication object. 
      DirContextOperations authAdapter = new DirContextAdapter(); 
      authAdapter.addAttributeValue("ldapContext", ldapContext); 

      return authAdapter; 
     } else { 
      throw new BadCredentialsException("Blank username and/or password!"); 
     } 
    } 

    /** 
    * Since the InitialLdapContext that's stored as a property of an LdapAuthenticationToken is 
    * transient (because it isn't Serializable), we need some way to recreate the 
    * InitialLdapContext if it's null (e.g., if the LdapAuthenticationToken has been serialized 
    * and deserialized). This is that mechanism. 
    * 
    * @param authenticator 
    *   the LdapAuthenticator instance from your application's context 
    * @param auth 
    *   the LdapAuthenticationToken in which to recreate the InitialLdapContext 
    * @return 
    */ 
    static public InitialLdapContext recreateLdapContext(LdapAuthenticator authenticator, 
      LdapAuthenticationToken auth) { 
     DirContextOperations authAdapter = authenticator.authenticate(auth); 
     InitialLdapContext context = (InitialLdapContext) authAdapter 
       .getObjectAttribute("ldapContext"); 
     auth.setContext(context); 
     return context; 
    } 

    public DefaultSpringSecurityContextSource getContextFactory() { 
     return contextFactory; 
    } 

    /** 
    * Set the context factory to use for generating a new LDAP context. 
    * 
    * @param contextFactory 
    */ 
    public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) { 
     this.contextFactory = contextFactory; 
    } 

    public String getPrincipalPrefix() { 
     return principalPrefix; 
    } 

    /** 
    * Set the string to be prepended to all principal names prior to attempting authentication 
    * against the LDAP server. (For example, if the Active Directory wants the domain-name-plus 
    * backslash prepended, use this.) 
    * 
    * @param principalPrefix 
    */ 
    public void setPrincipalPrefix(String principalPrefix) { 
     if (principalPrefix != null) { 
      this.principalPrefix = principalPrefix; 
     } else { 
      this.principalPrefix = ""; 
     } 
    } 

} 

И, наконец, класс LdapAuthenticationToken:

/** 
* <p> 
* Authentication token to use when an app needs further access to the LDAP context used to 
* authenticate the user. 
* </p> 
* 
* <p> 
* When this is the Authentication object stored in the Spring Security context, an application 
* can retrieve the current LDAP context thusly: 
* </p> 
* 
* <pre> 
* LdapAuthenticationToken ldapAuth = (LdapAuthenticationToken) SecurityContextHolder 
*  .getContext().getAuthentication(); 
* InitialLdapContext ldapContext = ldapAuth.getContext(); 
* </pre> 
* 
* @author Jason 
* 
*/ 
public class LdapAuthenticationToken extends AbstractAuthenticationToken { 

    private static final long serialVersionUID = -5040340622950665401L; 

    private Authentication auth; 
    transient private InitialLdapContext context; 
    private List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); 

    /** 
    * Construct a new LdapAuthenticationToken, using an existing Authentication object and 
    * granting all users a default authority. 
    * 
    * @param auth 
    * @param defaultAuthority 
    */ 
    public LdapAuthenticationToken(Authentication auth, GrantedAuthority defaultAuthority) { 
     this.auth = auth; 
     if (auth.getAuthorities() != null) { 
      this.authorities.addAll(Arrays.asList(auth.getAuthorities())); 
     } 
     if (defaultAuthority != null) { 
      this.authorities.add(defaultAuthority); 
     } 
     super.setAuthenticated(true); 
    } 

    /** 
    * Construct a new LdapAuthenticationToken, using an existing Authentication object and 
    * granting all users a default authority. 
    * 
    * @param auth 
    * @param defaultAuthority 
    */ 
    public LdapAuthenticationToken(Authentication auth, String defaultAuthority) { 
     this(auth, new GrantedAuthorityImpl(defaultAuthority)); 
    } 

    public GrantedAuthority[] getAuthorities() { 
     GrantedAuthority[] authoritiesArray = this.authorities.toArray(new GrantedAuthority[0]); 
     return authoritiesArray; 
    } 

    public void addAuthority(GrantedAuthority authority) { 
     this.authorities.add(authority); 
    } 

    public Object getCredentials() { 
     return auth.getCredentials(); 
    } 

    public Object getPrincipal() { 
     return auth.getPrincipal(); 
    } 

    /** 
    * Retrieve the LDAP context attached to this user's authentication object. 
    * 
    * @return the LDAP context 
    */ 
    public InitialLdapContext getContext() { 
     return context; 
    } 

    /** 
    * Attach an LDAP context to this user's authentication object. 
    * 
    * @param context 
    *   the LDAP context 
    */ 
    public void setContext(InitialLdapContext context) { 
     this.context = context; 
    } 

} 

Вы заметите, что есть несколько бит, которые вам могут не понадобиться.

Например, моему приложению необходимо сохранить успешно зарегистрированный контекст LDAP для дальнейшего использования пользователем после входа в систему - цель приложения - позволить пользователям войти в систему через свои учетные данные AD, а затем выполнить дальнейшие AD- связанных функций. Поэтому из-за этого у меня есть собственный токен аутентификации LdapAuthenticationToken, который я передаю (вместо токена аутентификации по умолчанию Spring), который позволяет мне привязать контекст LDAP. В LdapAuthenticationProvider.authenticate() я создаю этот токен и передаю его обратно; в LdapAuthenticatorImpl.authenticate() я присоединяю подключенный контекст к возвращаемому объекту, чтобы его можно было добавить к объекту проверки подлинности Spring пользователя.

Кроме того, в LdapAuthenticationProvider.authenticate() я назначил всем зарегистрированным пользователям роль ROLE_USER - вот что позволяет мне проверить эту роль в моих элементах перехвата-url. Вы хотите, чтобы это совпадение выполнялось независимо от той роли, которую вы хотите протестировать, или даже назначать роли на основе групп Active Directory или что-то еще.

И, наконец, как следствие того, как я реализовал LdapAuthenticationProvider.authenticate(), все пользователи с действительными учетными записями AD имеют одинаковую роль ROLE_USER. Очевидно, что в этом методе вы можете выполнять дополнительные тесты для пользователя (т. Е. Является пользователем в определенной группе AD?) И назначать роли таким образом или даже тестировать какое-либо условие, прежде чем даже предоставить пользователю доступ к всем.

2

Просто довести это до статуса уточненного. Spring Security 3.0 имеет complete package с реализацией по умолчанию, посвященной ldap-bind, а также проверяет и сравнивает аутентификацию.

0

Аутентификация LDAP без SSL небезопасна, каждый может видеть учетные данные пользователя, когда они перенаправляются на сервер LDAP. Я предлагаю использовать протокол LDAPS: \ для аутентификации. Это не требует каких-либо серьезных изменений в весенней части, но вы можете столкнуться с некоторыми проблемами, связанными с сертификатами. См. LDAP Active Directory authentication in Spring with SSL для получения более подробной информации.

0

От ответа Луки выше:

Для справки, Spring Security 3.1 имеет поставщика проверки подлинности [специально для Active Directory] [1].

[1]: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

Я попытался выше Spring Security 3.1.1: Есть некоторые незначительные изменения по сравнению с LDAP - активные группы каталогов пользователь является членом прийти через как оригинальный случай ,

Раньше под ldap группы были заглавными и с префиксом «ROLE_», что облегчало их поиск с помощью текстового поиска в проекте, но, очевидно, могли возникнуть проблемы в группе unix, если по какой-то странной причине было только две отдельные группы (например, счета и счета).

Также синтаксис требует ручной спецификации имени и порта контроллера домена, что делает его немного страшным для избыточности. Конечно, есть средство для поиска записи SRV DNS для домена в Java, то есть эквивалент (от Samba 4 HOWTO):

$ host -t SRV _ldap._tcp.samdom.example.com. 
_ldap._tcp.samdom.example.com has SRV record 0 100 389 samba.samdom.example.com. 

с последующим регулярным A поиска:

$ host -t A samba.samdom.example.com. 
samba.samdom.example.com has address 10.0.0.1 

(на самом деле может потребоваться также поиск записи в формате _kerberos SRV ...)

Вышеупомянутое было с Samba4.0rc1, мы постепенно обновляемся от среды Samba 3.x LDAP до Samba AD.

0

Как и в ответ Луки выше:

Spring Security 3.1 имеет поставщика проверки подлинности в частности для Active Directory.

Подробнее о том, как это можно легко сделать с помощью ActiveDirectoryLdapAuthenticationProvider.

В ресурсах.заводной:

ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider, 
     "mydomain.com", 
     "ldap://mydomain.com/" 
) 

В Config.groovy:

grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1'] 

Это весь код, что вам нужно. Вы можете в значительной степени удалить все другие параметры grails.plugin.springsecurity.ldap. * В Config.groovy, поскольку они не применяются к этой настройке AD.

Для документации см: http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory

0

Если вы используете Spring безопасности 4 вы также можете реализовать то же самое с помощью данного класса

  • SecurityConfig.java
@Configuration 
@EnableWebSecurity 
public class SecurityConfig extends WebSecurityConfigurerAdapter { 


static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class); 

@Autowired 
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 
    auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider()); 
} 

@Override 
protected void configure(HttpSecurity http) throws Exception { 
    http 
      .authorizeRequests() 
       .antMatchers("/").permitAll() 
       .anyRequest().authenticated(); 
      .and() 
       .formLogin() 
      .and() 
       .logout(); 
} 

@Bean 
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() { 
    ActiveDirectoryLdapAuthenticationProvider authenticationProvider = 
     new ActiveDirectoryLdapAuthenticationProvider("<domain>", "<url>"); 

    authenticationProvider.setConvertSubErrorCodesToExceptions(true); 
    authenticationProvider.setUseAuthenticationRequestCredentials(true); 

    return authenticationProvider; 
} 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^