Весенняя аутентификация LDAP с несколькими пользовательскими подразделениями и CNS множественного доступа

#java #spring #authentication #spring-security #ldap

#java #spring #аутентификация #spring-безопасность #ldap

Вопрос:

Как мне решить следующую ситуацию аутентификации LDAP, используя Spring Security / LDAP в максимально возможной степени.

  • Пользователь принадлежит к одному из 2 организационных подразделений LDAP (ou): клиенты или сотрудники

  • Пользователь принадлежит к одной из 3 групп доступа (cn — groupofuniquenames) или их подгрупп (cn)

Так что в основном это было бы:

  1. Поиск DN пользователя в LDAP (клиент или сотрудник)

  2. Привязка пользователя к проверке пароля

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

Я просмотрел различные учебные пособия и примеры, но ни один из них, похоже, не связан, и я не смог их объединить. Было бы проще, если бы access group была организационной единицей, но это не так.

Предполагается, что вся страница и все ее сервлеты должны быть аутентифицированы.

Вопрос немного специфичен, но, надеюсь, полезен для сообщества. Любые идеи или предложения приветствуются.

Код, который я использую в настоящее время, является модифицированной версией из документации spring.

 <bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
    <property name="rolePrefix" value=""></property>
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <constructor-arg name="decisionVoters" ref="roleVoter" />
</bean>

<security:http authentication-manager-ref="ldap-auth" access-decision-manager-ref="accessDecisionManager">
    <security:intercept-url pattern="/site/**" access="LDAP-Access-Group" />
    <security:form-login 
        login-page="/login" 
        authentication-failure-url="/denied"
        username-parameter="username" 
        password-parameter="password"
        default-target-url="/site/main" />
    <security:logout 
        invalidate-session="true" 
        logout-success-url="/login" 
        logout-url="/j_spring_security_logout" />
    <security:access-denied-handler error-page="/denied" />
    <security:session-management invalid-session-url="/login">
        <security:concurrency-control max-sessions="1" expired-url="/login" />            
    </security:session-management>
</security:http>

<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://server:389/o=company,c=com"/>
</bean>

<security:authentication-manager id="ldap-auth">            
    <security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
    <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
        <constructor-arg ref="contextSource"/>
        <property name="userDnPatterns">
            <list>
                <value>uid={0},ou=Employees</value>
                <value>uid={0},ou=Clients</value>
            </list>
        </property>
    </bean>
</constructor-arg>
<constructor-arg>
    <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
        <constructor-arg ref="contextSource"/>
        <constructor-arg value="ou=Access"/>
        <property name="searchSubtree" value="true"/>
        <property name="groupRoleAttribute" value="cn" />
    </bean>
</constructor-arg>
</bean>
  

Приведенный выше код, похоже, не возвращает полномочия. Есть ли способ отправить вывод на консоль отладки? Не удается прочитать журналы LDAP.

Кроме того, если я закомментирую AuthoritiesPopulator, то аутентификация, похоже, работает при проверке с помощью тегов безопасности, т. Е. <sec:authorize access="isAuthenticated()">logged in</sec:authorize> , Но по какой-то причине intercept-url не позволяет мне входить на сайт с помощью <security:intercept-url pattern="/site/**" access="isAuthenticated()" /> . Я этого не понимаю.

Комментарии:

1. Я думаю, вы имеете в виду, что пользователь находится в одном из двух контейнеров LDAP (подразделение обычно является контейнером). Кроме того, является ли ваш сервер LDAP активным каталогом?

2. @mvreijn Это не AD.

3. ХОРОШО, это исключает msdn.microsoft.com/en-us/library/aa746475(VS.85).aspx

Ответ №1:

Ваши шаги 1 и 2 являются практическими по умолчанию для аутентификации LDAP.

Что касается шага 3: некоторые серверы LDAP поддерживают взаимные отношения для групп и их членов. Поэтому, когда пользователь добавляется в группу, заполняются как member атрибут для группы, так и memberOf атрибут для пользователя. Это упростило бы задачу, потому что затем вы можете получить атрибут пользователей memberOf и найти свои группы.

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

Затем запросите сервер LDAP для всех групп, в которых ваш пользователь является членом, используя (amp;(objectClass=groupOfUniqueNames)(member=cn=youruser,ou=some,o=org)) возврат только списка DN, без атрибутов (по соображениям производительности).

Теперь вы можете просмотреть список возвращенных групп и посмотреть, есть ли среди них нужные вам группы.

Этот подход гарантирует, что вам потребуется всего один запрос во время входа в систему (при условии, что вы кэшируете список групп), а не по одному на группу.

Комментарии:

1. С атрибутом memberOf это было бы слишком просто, и у меня его здесь нет. Итак, в основном мне нужно написать свой пользовательский менеджер аутентификации, и нет волшебной конфигурации безопасности Spring, которая работала бы в этом сценарии?

2. Я думаю, что вам не нужно кодировать все это, когда вы используете Spring. Если вы настроите group-search-base и установите role-prefix="none" , вы получите список групп, членом которых является пользователь. Однако Spring предполагает, что эти авторизации должны быть проверены после аутентификации . Вам нужно будет проверить предоставленные полномочия для использования вашими группами LdapAuthenticationProvider.loadUserAuthorities() .

Ответ №2:

Похоже, основная проблема заключалась в указании базового аргумента поиска в DefaultLdapAuthoritiesPopulator . Изменение значения на «» в компоненте DefaultLdapAuthoritiesPopulator решило проблему и начало возвращать полномочия пользователя.

 <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <constructor-arg value="ldap://server:389/o=company,c=com"/>
    <property name="anonymousReadOnly" value="true"/>
</bean>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
    <constructor-arg>
        <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
            <constructor-arg ref="contextSource"/>
            <property name="userDnPatterns">
                <list>
                    <value>uid={0},ou=Employees</value>
                    <value>uid={0},ou=Clients</value>
                </list>
            </property>         
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
            <constructor-arg ref="contextSource"/>
            <constructor-arg value=""/>
            <property name="searchSubtree" value="true"/>
            <property name="groupRoleAttribute" value="cn"/>
            <property name="groupSearchFilter" value="uniquemember={0}"/>   
        </bean>
    </constructor-arg>
</bean>