Подтвердить что ты не робот

Spring Безопасность 4.0.0 + ActiveDirectoryLdapAuthenticationProvider + BadCredentialsException PartialResultException

Я прочитал почти все о Spring/Security/Ldap и ActiveDirectory в stackoverflow. Даже если бы я нашел полезные советы и подсказки, я не смог решить свою проблему.

Вот он: я настроил Spring Безопасность, используя пользовательскую службу и пользовательскую страницу входа, и все работает нормально. Затем я попытался переключиться на конечный поставщик аутентификации, который, как оказалось, является ActiveDirectory.

Вот мой security-applicationContext.xml(напомню, что эта настройка отлично работает с пользовательской услугой в качестве поставщика проверки подлинности, поэтому файл импортируется и т.д.):

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/security"
    xmlns:oauth="http://www.springframework.org/schema/security/oauth"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/security/oauth http://www.springframework.org/schema/security/spring-security-oauth.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<!-- Définitions globales de sécurité -->
<global-method-security pre-post-annotations="enabled" />

<!-- Configuration de l'accès et du formulaire -->
<!-- Permettre l'accès libre aux feuilles de style, polices et images -->
<http pattern='/resources/css/**' security="none" />
<http pattern='/resources/fonts/**' security="none" />
<http pattern='/resources/images/**' security="none" />

<http use-expressions="true" access-denied-page="/403" disable-url-rewriting="true">

    <!-- Limitation à une seule session utilisateur concurrente -->
    <session-management invalid-session-url="/identite?time=1">
        <concurrency-control max-sessions="1" expired-url="/identite?time=1" />
    </session-management>

    <!-- Définitions pour le formulaire de la page JSP d'identification -->
    <form-login login-page="/identite" login-processing-url="/identite.proc" default-target-url="/" always-use-default-target="true" authentication-failure-url="/identite?err=1" username-parameter="username" password-parameter="password" />

    <logout logout-url="/logout" logout-success-url="/identite?out=1" delete-cookies="JSESSIONID" invalidate-session="true" />

    <!-- Utiliser un canal chiffré pour les échanges -->
    <intercept-url requires-channel="https" pattern="/identite*" access="permitAll()" />
    <intercept-url requires-channel="https" pattern="/**" access="isAuthenticated()" />
</http>

<!-- Fournisseurs d'identité pour le formulaire (au final, LDAP) -->
<authentication-manager erase-credentials="true">
    <ldap-authentication-provider ref="myADProvider" />
    <!-- This is the user-service authentication provider that is working fine
        <authentication-provider>
        <user-service>
        <user name="Toto" authorities="ROLE_USER, ROLE_ADMIN" password="totototo" />
        </user-service>
        </authentication-provider>
    -->
</authentication-manager>

<b:bean id="myADProvider"
    class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <b:constructor-arg value="fsappsuni" />
    <b:constructor-arg value="ldap://fsapps.company.uni:389/" />
    <b:property name="convertSubErrorCodesToExceptions" value="true" />
    <b:property name="useAuthenticationRequestCredentials" value="true" />
</b:bean>

В bean myADProvider, даже если я изменил первый аргумент конструктора на fsapps.company.uni или company.uni или что бы то ни было, он ничего не изменит при следующей ошибке. Проблема возникает из-за того, что после привязки поиск выполняется с неправильным фильтром, ища что-то вроде (& (userPrincipalName = {0}) (objectclass= user)) вместо (& (sAMAccountName = {0}) (объектный = пользователь)). Поскольку я не мог понять, как изменить это, пока вы можете сделать это с поставщиком LDAP, я затем переключился на провайдера LDAP, пытаясь заставить его работать без успеха, не наткнувшись на проблему в том же месте. Я также обновил мою Spring Framework с 3.1.2 до 4.1.6.RELEASE и Spring Безопасность с 3.1.2 до 4.0.0.RELEASE после прочтения возникла проблема с ActiveDirectoryLdapAuthenticationProvider в надежде, что проблема будет в то же время, начальные обсуждения этого вопроса с версией 3.x.

Вот моя конфигурация для настройки LDAP, которая пытается сделать аутентификацию работой с Active Directory:

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/security"
    xmlns:oauth="http://www.springframework.org/schema/security/oauth"
    xsi:schemaLocation="http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd
    http://www.springframework.org/schema/security/oauth
    http://www.springframework.org/schema/security/spring-security-oauth.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

<!-- Définitions globales de sécurité -->
<global-method-security pre-post-annotations="enabled" />

<!-- Configuration de l'accès et du formulaire -->
<!-- Permettre l'accès libre aux feuilles de style, polices et images -->
<http pattern='/resources/css/**' security="none" />
<http pattern='/resources/fonts/**' security="none" />
<http pattern='/resources/images/**' security="none" />

<http use-expressions="true" disable-url-rewriting="true">

    <!-- Limitation à une seule session utilisateur concurrente -->
    <session-management invalid-session-url="/identite?time=1">
        <concurrency-control max-sessions="1" expired-url="/identite?time=1" />
    </session-management>

    <!-- Définitions pour le formulaire de la page JSP d'identification -->
    <form-login login-page="/identite" login-processing-url="/identite.proc" default-target-url="/" always-use-default-target="true" authentication-failure-url="/identite?err=1" username-parameter="username" password-parameter="password" />
    <csrf disabled="true" />

    <logout logout-url="/logout" logout-success-url="/identite?out=1" delete-cookies="JSESSIONID" invalidate-session="true" />

    <!-- Utiliser un canal chiffré pour les échanges -->
    <intercept-url requires-channel="https" pattern="/identite*" access="permitAll()" />
    <intercept-url requires-channel="https" pattern="/**" access="isAuthenticated()" />
</http>

<!-- Fournisseurs d'identité pour le formulaire (au final, LDAP) -->
<ldap-server url="ldap://fsapps.company.uni/dc=fsapps,dc=company,dc=uni" port="389" />
<authentication-manager erase-credentials="true">
    <ldap-authentication-provider role-prefix="none"
        user-search-filter="(&amp;(sAMAccountName={0})(objectClass=user))"
        group-search-filter="(&amp;(member={0})(objectClass=group))"
        user-search-base="dc=fsapps,dc=company,dc=uni">
    </ldap-authentication-provider>
    <!-- <authentication-provider ref="myADProvider" ></authentication-provider> -->
</authentication-manager>

<!--
<b:bean id="myADProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <b:constructor-arg value="fsapps.company.uni" />
    <b:constructor-arg value="ldap://fsapps.company.uni:389/" />
    <b:property name="convertSubErrorCodesToExceptions" value="true" />
    <!- -
    <b:property name="useAuthenticationRequestCredentials" value="true" />
    - ->
</b:bean>
<b:bean id="webSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"></b:bean>
-->

</b:beans>

На этот раз я оставил часть конфигурации bean с поставщиком AD для справки. Это тоже не работает.

Итак, как я могу передать пути поиска пользователей и групп поставщику AD? Или, альтернативно, как настроить LDAP-провайдера для работы с AD и полной аутентификации?

Вот сообщение, которое я получаю в своем журнале с настройкой LDAP, настройка AD объясняется выше (Bad Credentials по существу из-за неправильного пути поиска):

2015-04-15 16:19:13,252 DEBUG (o.s.s.a.ProviderManager.authenticate) [http-8443-1] Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider MDC{}
2015-04-15 16:19:13,252 DEBUG (o.s.s.l.a.AbstractLdapAuthenticationProvider.authenticate) [http-8443-1] Processing authentication request for user: ba5glag MDC{}
2015-04-15 16:19:13,252 DEBUG (o.s.s.l.s.FilterBasedLdapUserSearch.searchForUser) [http-8443-1] Searching for user 'myuser', with user search [ searchFilter: '(&(sAMAccountName={0})(objectClass=user))', searchBase: 'dc=fsapps,dc=company,dc=uni', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ] MDC{}
2015-04-15 16:19:13,299 DEBUG (o.s.s.a.DefaultAuthenticationEventPublisher.publishAuthenticationFailure) [http-8443-1] No event was found for the exception org.springframework.security.authentication.InternalAuthenticationServiceException MDC{}
2015-04-15 16:19:13,299 ERROR (o.s.s.w.a.AbstractAuthenticationProcessingFilter.doFilter) [http-8443-1] An internal error occurred while trying to authenticate the user. MDC{}
org.springframework.security.authentication.InternalAuthenticationServiceException: Uncategorized exception occured during LDAP processing; nested exception is javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr: DSID-0C090627, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, vece(unprintable character here)]; remaining name 'dc=fsapps,dc=company,dc=uni'
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.doAuthentication(LdapAuthenticationProvider.java:207) ~[spring-security-ldap-4.0.0.RELEASE.jar:?]
        at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:82) ~[spring-security-ldap-4.0.0.RELEASE.jar:?]

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

ОБНОВЛЕНИЕ 2015-04-16 ~ 10: 20

Я выяснил, как добавить фильтр поиска с помощью ActiveDirectoryLdapAuthenticationProvider. Я также наблюдал с Wireshark обмен между моим сервером приложений и сервером AD, чтобы увидеть, что на самом деле было сделано. Вот мои выводы, я считаю, что я не далеко от решения моей проблемы. Мой обновленный security-applicationContext.xml, я выбросил материал ldap-сервера, так как теперь я сосредоточен на том, чтобы работать с ActiveDirectoryLdapAuthenticationProvider. Итак, теперь это более чистая конфигурация для чтения:

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/security"
    xmlns:oauth="http://www.springframework.org/schema/security/oauth"
    xsi:schemaLocation="http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd
    http://www.springframework.org/schema/security/oauth
    http://www.springframework.org/schema/security/spring-security-oauth.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

    <!-- Définitions globales de sécurité -->
    <global-method-security pre-post-annotations="enabled" />

    <!-- Configuration de l'accès et du formulaire -->
    <!-- Permettre l'accès libre aux feuilles de style, polices et images -->
    <http pattern='/resources/css/**' security="none" />
    <http pattern='/resources/fonts/**' security="none" />
    <http pattern='/resources/images/**' security="none" />
    <http pattern='/resources/js/**' security="none" />

    <http use-expressions="true" disable-url-rewriting="true">

        <!-- Limitation à une seule session utilisateur concurrente -->
        <session-management invalid-session-url="/identite?expiree=1">
            <concurrency-control max-sessions="1" expired-url="/identite?expiree=1" />
        </session-management>

        <!-- Définitions pour le formulaire de la page JSP d'identification -->
        <form-login login-page="/identite" login-processing-url="/identite.proc" default-target-url="/" always-use-default-target="true" authentication-failure-url="/identite?err=1" username-parameter="username" password-parameter="password" />
        <csrf disabled="true" />

        <logout logout-url="/logout" logout-success-url="/identite?termine=1" delete-cookies="JSESSIONID" invalidate-session="true" />

        <!-- Utiliser un canal chiffré pour les échanges -->
        <intercept-url requires-channel="https" pattern="/identite*" access="permitAll()" />
        <intercept-url requires-channel="https" pattern="/**" access="isAuthenticated()" />
        <access-denied-handler error-page="/erreur403" />
    </http>

    <!-- Fournisseurs d'identité pour le formulaire (au final, LDAP) -->
    <authentication-manager erase-credentials="true">
         <authentication-provider ref="myADProvider" />
    </authentication-manager>

    <b:bean id="myADProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
        <b:constructor-arg value="fsapps.company.uni" />
        <b:constructor-arg value="ldap://fsapps.company.uni:389/" />
        <b:property name="searchFilter" value="(&amp;(sAMAccountName={0})(objectClass=user))" />
        <b:property name="convertSubErrorCodesToExceptions" value="true" />
        <b:property name="useAuthenticationRequestCredentials" value="true" />
    </b:bean>
    <b:bean id="webSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />

</b:beans>

Несколько комментариев о конфигурации. В определении myADProvider bean первый аргумент конструктора fsapps.company.uni используется для создания принципала в форме [email protected] и для создания базового объекта для запроса поиска с базовым объектом формы: dc = fsapps, DC = компания, dc = уни.

Второй аргумент конструктора используется для установления связи. Следующий элемент типа свойства с name= "searchFilter" вызовет метод setSearchFilter() класса ActiveDirectoryLdapAuthenticationProvider, чтобы установить фильтр поиска (именно это я искал изначально, чтобы добиться определенного прогресса). Итак, теперь он настроен на поиск (& (sAMAccountName = {0}) (objectclass= пользователь)).

С помощью этой настройки я вижу в WireShark цепочку LDAP, и привязка завершается успешно. Ответ на связывание является успешным. Затем запрос на поиск также успешно завершен, но не возвращает результат. Следовательно, я все еще получаю следующее в своем журнале:

2015-04-16 10:30:28,201 DEBUG (o.s.s.a.ProviderManager.authenticate) [http-8443-2] Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider MDC{}
2015-04-16 10:30:28,201 DEBUG (o.s.s.l.a.AbstractLdapAuthenticationProvider.authenticate) [http-8443-2] Processing authentication request for user: myusername MDC{}
2015-04-16 10:30:28,294 DEBUG (o.s.s.l.SpringSecurityLdapTemplate.searchForSingleEntryInternal) [http-8443-2] Searching for entry under DN '', base = 'dc=fsapps,dc=company,dc=uni', filter = '(&(sAMAccountName={0})(objectClass=user))' MDC{}
2015-04-16 10:30:28,294 INFO (o.s.s.l.SpringSecurityLdapTemplate.searchForSingleEntryInternal) [http-8443-2] Ignoring PartialResultException MDC{}
2015-04-16 10:30:28,310 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-2] Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials MDC{}
2015-04-16 10:30:28,310 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-2] Updated SecurityContextHolder to contain null Authentication MDC{}
2015-04-16 10:30:28,310 DEBUG (o.s.s.w.a.AbstractAuthenticationProcessingFilter.unsuccessfulAuthentication) [http-8443-2] Delegating to authentication failure handler org.springframework.se[email protected]2876b359 MDC{}

Я считаю, что я почти там, но все же могу использовать небольшую помощь, если кто-то может предоставить любой. После тщательного изучения появляется первый аргумент конструктора, который используется для построения baseObject. И userPrincipalName - моя проблема. В нашей настройке имя userPrincipalName использует имя домена, которое не является именем домена в URL-адресе LDAP (например, campus.company.com). Пока что так хорошо, я могу изменить аргумент, чтобы соответствовать нашему доменному имени, используемому для построения принципов наших пользователей. Теперь проблема заключается в изменении базового объекта, который должен соответствовать схеме URL-адреса LDAP (то есть fsapps.company.uni).

В документации есть только один способ установки фильтра поиска, однако конструктор может использовать один, два или три аргумента. Третий аргумент предоставляет значение baseObject.

Затем моя проблема решается следующей конфигурацией, где я должен использовать все доступные сеттеры и аргументы конструктора, чтобы заставить ее работать. Определение myADProvider bean выглядит следующим образом:

    <b:bean id="myADProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
        <b:constructor-arg value="campus.company.com" />
        <b:constructor-arg value="ldap://fsapps.company.uni:389/" />
        <b:constructor-arg value="dc=fsapps,dc=company,dc=uni" />
        <b:property name="searchFilter" value="(&amp;(userPrincipalName={0})(objectClass=user))" />
        <b:property name="convertSubErrorCodesToExceptions" value="true" />
    </b:bean>

В моем случае свойство searchFilter могло быть опущено сейчас, так как я возвращаюсь к значению по умолчанию. Несмотря на это очень длинное описание моей установки и проблемы. Я надеюсь, что кто-то другой сможет извлечь из этого выгоду.

Вот ссылка на документацию класса ActiveDirectorLdapAuthenticationProvider: http://docs.spring.io/autorepo/docs/spring-security/4.0.0.RELEASE/apidocs/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.html

Я нашел WireShark очень полезным, чтобы узнать, что происходит между сервером приложений и сервером AD для отладки этой проблемы.

4b9b3361

Ответ 1

Я не смог решить эту проблему, используя Context.REFERRAL = "follow" на самом деле проблема заключается в коде метода searchForUser() класса ActiveDirectoryLdapProvider. В этом методе метод SpringSecurityLdapTemplate.searchForSingleEntryInternal() вызывается с bindPrincipal, который на самом деле является userPrincipalName, состоящим из аргументов, переданных конструктору в первом аргументе и имени пользователя. Таким образом, даже если вы установите свой фильтр поиска на что-либо еще, кроме userPrincipalName, ему будет передано имя userPrincipalName в качестве аргумента 0. Следовательно, фильтр с именем sAMAccountName не будет работать с UPN и выбросить исключение.

Любая searchForUser() должна быть изменена или дополнена, чтобы обнаружить, что searchFilter нуждается в имени пользователя, а не в UPN, либо предоставляются дополнительные сеттеры для установки аргументов с использованием шаблонов для searchFilter.

Но нет такой возможности, чтобы этот класс работал правильно в такой ситуации без изменения кода. Это то, что я наконец сделал. Я написал собственный класс, в основном, копию исходного ActiveDirectoryLdapAUthenticationProvider с одной простой и простой модификацией для searchForUser(), передающей имя пользователя вместо bindPrincipal для searchForSingleEntryInternal().

Немного глупость, вы можете ввести любой поисковый фильтр, который вам нужен, но вынуждены использовать только один аргумент, который фактически является именем пользователяPrincipalName и ничего более.

Ответ 2

Я как-то столкнулся с подобной проблемой и сделал некоторые исследования. В нашем AD с доменом "my.company.com" у нас, казалось, было два набора пользователей, один из которых установлен с UPN в формате [email protected], а в другом случае он в виде user2 @somethingelse. ком.

И когда я использую org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider и пытаюсь аутентифицировать пользователей с [email protected] и [email protected] - только один из них работает в зависимости от того, что я прохожу к конструктору для имени домена.

Я не эксперт по AD, но смотрю на статью Microsoft Tech net: https://technet.microsoft.com/en-us/library/cc739093(v=ws.10).aspx она выглядит как в то время как суффикс UPN обычно такой же, как домен имя, оно не является обязательным и может быть дополнено чем-то другим. Цитируя эту статью

Вторая часть UPN, суффикс UPN, идентифицирует домен в который находится в учетной записи пользователя. Этот суффикс UPN может быть DNS доменное имя, DNS-имя любого домена в лесу или оно может быть альтернативное имя, созданное администратором и используемое только для входа в систему цели. Этот альтернативный суффикс UPN не должен быть действительным DNS имя.

В Active Directory суффикс UPN по умолчанию является DNS-именем домен, в котором создана учетная запись пользователя. В большинстве случаев это доменное имя, зарегистрированное как домен предприятия в Интернете. С помощью альтернативные имена доменов в качестве суффикса UPN могут предоставить дополнительные безопасность входа в систему и упростить имена, используемые для входа в другой домен в лесу.

Например, если ваша организация использует глубокое дерево доменов, организованное по отделам и регионам, доменные имена могут занять довольно много времени. По умолчанию пользовательский UPN для пользователя в этом домене может быть sales.westcoast.microsoft.com. Имя входа для пользователя в этом домен будет [email protected] Создание UPN суффикс "microsoft" позволит тому же пользователю войти в систему, используя гораздо проще имя входа пользователя @microsoft. Для получения дополнительной информации о учетные записи пользователей, см. Учетные записи пользователей и компьютеров и имена объектов.

Но я думаю, что ActiveDirectoryLdapAuthenticationProvider.java, похоже, делает предположение, что доменное имя такое же, как суффикс UPN. Я сделал локальное исправление для ActiveDirectoryLdapAuthenticationProvider.java, чтобы не делать этого предположения:

String createBindPrincipal(String username) {
    if (domain == null || username.toLowerCase().endsWith(domain) || username.contains("@")) {
            return username;
    }
    return username + "@" + domain;
}

Теперь оба пользователя с их различными суффиксами UPN доступны для поиска. Если мои предположения верны, я могу открыть ошибку с безопасностью Spring.

Ответ 3

В среде Spring Security 4.1.1/SpringBoot 1.4.0 я делаю это так (на Java):

@Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter
{
   public void init (AuthenticationManagerBuilder aAuth) throws Exception
   {

      ActiveDirectoryLdapAuthenticationProvider
              myProvider = new ActiveDirectoryLdapAuthenticationProvider (ldapDomain, ldapUrl);
      aAuth.authenticationProvider (myProvider);
      aAuth.eraseCredentials (false);
   }
}

Я не сталкиваюсь с какой-либо проблемой, и пользователь может войти в систему с помощью sAMAccountName.