Обеспечение безопасности Java-приложений с помощью Acegi: Часть 2. Работа с сервером каталогов LDAP

Управление доступом с помощью ApacheDS и Acegi

Познакомившись с основами, можно продолжить изучение более продвинутых вариантов использования системы безопасности Acegi. В этой статье Билал Сиддикви покажет, как можно интегрировать Acegi с сервером каталогов LDAP для построения гибкой, высокопроизводительной системы безопасности Java™-приложений. Вы узнаете, как написать политику управления доступом и сохранить ее в ApacheDS, а затем настроить взаимодействие Acegi с сервером каталогов для решения задач аутентификации и авторизации.

Билал Сиддикви, внештатный консультант, WaxSys

Билал Сиддикви (Bilal Siddiqui) является инженером-электронщиком, консультантом по XML и соучредителем WaxSys, компании, чья деятельность направлена на упрощение электронного бизнеса. После окончания в 1995 г. Инженерно-технологического Университета, г. Лахор, и получения степени по электронной технике, он начал разрабатывать программные продукты для промышленных систем управления. В дальнейшем он занимался XML и использовал свой опыт в программировании C++ для разработки Web- и Wap-базируемых инструментов для XML-технологий, серверных парсинговых программных продуктов и служебных приложений. Билал – проповедник передовых технологий и часто публикуется в этой области.



02.06.2008

Этот цикл из трех статей представляет собой введение в использование системы безопасности Acegi (Acegi Security System) для обеспечения безопасности корпоративных Java-приложений. В первой статье этого цикла мы познакомились с Acegi и разобрали, как использовать фильтры безопасности для реализации простой системы безопасности, основанной на URL. В этой второй статье мы начнем обсуждение более продвинутых вариантов использования Acegi, начиная с написания политики управления доступом и сохранения ее в ApacheDS - сервере каталогов LDAP с открытым кодом. Также будет показано, как настроить взаимодействие Acegi с сервером каталогов для реализации политики управления доступом. В заключение этой статьи будет представлен пример приложения, использующего ApacheDS и Acegi для реализации безопасной политики управления доступом.

Реализация политики управления доступом обычно состоит из двух шагов:

  1. Сохранение информации о пользователях и их ролях на сервере каталогов.
  2. Написание кода для обеспечения безопасности, определяющего, кто может получить доступ к данным и использовать их.

Acegi освобождает от написания кода, так что в этой статье будет сначала показано, как сохранить информацию о пользователях и их ролях в ApacheDS, а затем как реализовать политику управления доступом на основе этой информации. В последней статье этого цикла будет рассказано, как настроить Acegi для обеспечения безопасного доступа к Java-классам.

Скачать пример приложения можно в любой момент изучения статьи. В разделе Ресурсы приведены ссылки для скачивания Acegi, Tomcat и ApacheDS, которые потребуются для запуска примеров кода и примера приложения.

Основы LDAP

Lightweight Directory Access Protocol (LDAP – упрощенный протокол для доступа к каталогам) - возможно, самый популярный протокол, определяющий форматы данных для стандартных операций над каталогом, таких как, чтение, редактирование, поиск и удаление информации, хранящейся в сервере каталогов. В этом разделе кратко объясняется, почему сервер каталогов лучше подходит для хранения информации о безопасности, чем файл свойств (properties file), и показывается, как организовать и разместить информацию о пользователях в каталоге LDAP.

Почему именно сервер каталогов?

В первой статье этого цикла был рассмотрен простой способ хранения информации о пользователе в виде формы в файле свойств (см. Часть 1. Листинг 6). В файле свойств имена пользователей, пароли и роли пользователей хранятся в текстовом формате. Для большинства реальных приложений – это неподходящий способ хранения информации о безопасности. По множеству причин сервер каталогов – это куда лучший выбор. Одна из причин в том, что реальные приложения могут использоваться большим числом пользователей, даже тысячами пользователей, особенно если приложение открывает часть своей функциональности клиентам и поставщикам. Часто выполнять поиск среди неорганизованной информации, хранящейся в текстовом файле, - это не очень эффективное решение, а сервер каталогов оптимизирован для подобного поиска.

Другую причину иллюстрирует файл свойств (см. Часть 1, Листинг 6), в котором одновременно хранится информация и о пользователях, и об их ролях. В реальном приложении для управления доступом обычно предпочитают определять и хранить информацию о пользователях и ролях по отдельности для облегчения поддержки базы пользователей. Сервер каталогов предоставляет почти неограниченную гибкость обновления и изменения информации о пользователях, например, для отслеживания повышений по службе или приема новых сотрудников. В разделе Ресурсы приведены ссылки на дополнительную информацию о преимуществах и использовании серверов каталогов.

Настройка каталога LDAP

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

Каталог LDAP хранит информацию в форме дерева элементов, как показано на рисунке 1:

Рисунок 1. Древовидная структура каталога LDAP
Рисунок 1. Древовидная структура каталога LDAP

На рисунке 1 корневым элементом является элемент org. Корневой элемент может содержать данные, относящиеся к разным предприятиям. Например, производственное предприятие, которым мы занимались в первой статье цикла, показано как прямой элемент-потомок корневого элемента org. У производственного предприятия есть два элемента-потомка, названные departments (отделы) и partners (партнеры).

Элемент-потомок partners содержит партнеров различных типов. Три из них, показанные на рисунке 1, - это customers (клиенты), employees (сотрудники) и suppliers (поставщики). Отметим, что каждый из этих трех типов партнеров может выступать в качестве пользователя корпоративной системы. У каждого типа пользователей есть собственная роль в бизнесе и, следовательно, различные права на доступ в систему.

Точно так же элемент departments содержит различные отделы производственного предприятия, например, дочерние элементы engineering (инженерный) и marketing (маркетинговый). Каждый элемент-отдел содержит одну или несколько групп пользователей. Как показано на рисунке 1, группа engineers (инженеры) - это дочерний элемент отдела engineering.

Таким образом, элементы-потомки каждого отдела представляют собой группу пользователей. Эти элементы-потомки содержат различных пользователей (членов этой группы). Например, все инженеры, работающие в инженерном отделе, входят в группу engineers в отделе engineering.

Также можно отметить последний элемент-потомок элемента departments на рисунке 1. Элемент specialUser представляет собой пользователя, а не группу. При такой конфигурации каталога пользователи, такие как alice и bob, обычно содержатся в элементе partners. Я добавил этого специального пользователя в элемент departments, чтобы продемонстрировать гибкость Acegi, позволяющую пользователям находиться в любом месте каталога LDAP. Далее в этой статье будет показано, как настроить Acegi для использования specialUser.

Использование уникальных имен

Для идентификации конкретных элементов в дереве LDAP в протоколе LDAP используется концепция уникального имени (distinguished name - DN). У каждого элемента есть уникальное DN, содержащее полную информацию о его иерархии. Например, на рисунке 2 показаны DN для некоторых элементов, представленных на рисунке 1:

Рисунок 2. Уникальные имена элементов в каталоге LDAP
Рисунок 2. Уникальные имена элементов в каталоге LDAP

Сначала отметим DN корневого элемента на рисунке 2. Его уникальное имя - dc=org, это пара атрибут-значение, связанная с корневым элементом org. Каждый элемент имеет несколько атрибутов, связанных с ним. Атрибут dc определяет свойство "доменный компонент" (domain component) и определен в спецификации LDAP RFC 2256 (в разделе Ресурсы приведены ссылки на официальную документацию RFC). Корневой элемент в каталоге LDAP обычно представляется как доменный компонент.

Каждый LDAP-атрибут определяется в некотором RFC. LDAP позволяет использовать для создания DN множество атрибутов, но в примерах в этой статье используются только следующие четыре атрибута:

  • dc (domain component – доменный компонент)
  • o (organization - организация)
  • ou (organizational unit – подразделение организации)
  • uid (user ID – идентификатор пользователя)

В примерах атрибут dc используется для задания домена, o для задания имени организации, ou для указания различных отделов организации и uid для пользователей.

Так как org – это корневой элемент, то в его DN нужно указать только его собственное имя (dc=org). Для сравнения, DN элемента manufacturingEnterprise имеет значение o=manufacturingEnterprise,dc=org. По мере продвижения вниз по элементам дерева DN каждого родительского элемента включается в DN его элементов-потомков.

Группировка атрибутов

LDAP группирует связанные типы атрибутов в виде классов объектов. Например, класс объектов organizationalPerson содержит все атрибуты, определяющие сотрудника организации: обращение, имя, почтовый адрес и т.д.

Классы объектов используют наследование, что позволяет LDAP определять базовые классы для хранения общих атрибутов. Классы-потомки затем наследуют базовым классам и используют атрибуты, объявленные в них. Отдельный элемент в каталоге LDAP может использовать несколько классов объектов. Примеры в этой статье используют следующие классы объектов:

  • Класс объектов top – это базовый класс для всех классов объектов в LDAP.
  • Класс объектов domain используется, когда другие классы объектов не подходят. В нем определен набор атрибутов, любой из которых может использоваться для определения объекта. Атрибут dc в этом классе является обязательным.
  • Класс объектов organization представляет элементы-организации, такие как элемент manufacturingEnterprise на рисунке 2.
  • Класс объектов organizationalUnit представляет подразделения организации, такие как элемент departments и его потомки на рисунок 1.
  • Класс объектов groupOfNames представляет группу имен, например имен людей, работающих в департаменте. У него есть атрибут member, содержащий список пользователей. Все групповые элементы на рисунке 1, такие как элемент engineers, используют атрибут member для определения членов группы. Более того, в примерах используется атрибут ou (organizational unit) класса объектов groupOfNames для определения бизнес-задачи группы.
  • Класс объектов organizationalPerson представляет сотрудника организации, такого как элемент alice на рисунке 1.

Работа с сервером LDAP

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

Как упоминалось ранее, чтобы продемонстрировать, как Acegi работает с каталогами LDAP, в примерах используется ApacheDS – сервер каталогов LDAP с открытым кодом. LDAP-клиент с открытым кодом – Jxplorer – используется для выполнения простых действий над каталогом, например, для сохранения информации в ApacheDS. В разделе Ресурсы приведена информация о том, как скачать ApacheDS и JXplorer и настроить их для совместной работы.

Создание корневого элемента в ApacheDS

Чтобы создать дерево элементов, показанное на рисунке 1, сначала необходимо создать корневой элемент org в ApacheDS. Для этой цели ApacheDS предоставляет конфигурационный XML-файл. В этом файле определен набор bean-компонентов, которые можно настроить для реализации в сервере каталогов поведения, отвечающего требованиям приложения. Ниже приведена конфигурация для создания корневого элемента.

Конфигурационный файл server.xml можно найти в каталоге conf в каталоге, куда был установлен ApacheDS. В этом файле находится набор конфигураций bean-компонентов, сходных с конфигурациями фильтров Acegi. Нас интересует bean-компонент examplePartitionsConfiguration. Этот компонент управляет разделами в ApacheDS. При создании нового корневого элемента фактически создается новый раздел в каталоге LDAP.

Внесем изменения в компонент examplePartitionConfiguration, чтобы создать корневой элемент org, как показано в листинге 1:

Листинг 1. Изменения в конфигурации bean-компонента examplePartitionConfiguration
<bean id="examplePartitionConfiguration" class=
"org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration"
>

<property name="suffix"><value>dc=org</value></property>

<property name="contextEntry">
<value>
objectClass: top
objectClass: domain
dc: org
</value>
</property>

<!-- другие параметры компонента examplePartitionConfiguration,
     которые редактировать не нужно. -->

</bean>

В листинге 1 изменяются два свойства компонента examplePartitionConfiguration:

  • свойство suffix, определяющее DN корневого элемента,
  • свойство contextEntry, определяющее класс объектов, который будет использоваться корневым элементом org. Корневой элемент org использует два типа классов объектов: top и domain.

The Исходный код этой статьи содержит отредактированную версию файла server.xml. Чтобы следить за примером, скопируйте файл server.xml из исходного кода туда, где он должен находиться – в каталог conf в установочном каталоге ApacheDS.

На рисунке 3 показан снимок с экрана, на котором JXplorer отображает корневой элемент после того, как он был создан в ApacheDS:

Рисунок 3. Корневой элемент в JXplorer
Рисунок 3. Корневой элемент в JXplorer

Заполнение сервера данными

Следующий этап в настройке LDAP сервера – это заполнение его информацией о пользователях и группах. Можно воспользоваться JXplorer для последовательного создания элементов в ApacheDS, но гораздо проще заполнить сервер с помощью формата для обмена данными в LDAP (LDAP Data Interchange Format - LDIF). LDIF – это известный формат, используемый во многих реализациях LDAP. Подготовка LDIF-файлов была подробно освещена в статьях developerWorks, так что эту тему мы разбирать не будем. В разделе Ресурсы приведены ссылки на дополнительную информацию по LDIF.

Вместо этого в доступном для загрузки исходном коде этой статьи можно посмотреть LDIF-файл, который представляет пользователей и отделы, показанные на рисунке 1. Этот LDIF-файл можно импортировать в ApacheDS с помощью JXplorer. Для импорта LDIF-файла необходимо использовать меню LDIF в JXplorer, как показано на рисунке 4:

Рисунок 4. Импорт LDIF-файла в ApacheDS
Рисунок 4. Импорт LDIF-файла в ApacheDS

После того как LDIF-файл будет импортирован в ApacheDS, JXplorer будет показывать дерево элементов пользователей и отделов, как показано на рисунке 1. Теперь можно переходить к настройке Acegi для взаимодействия с LDAP-сервером.


Настройка Acegi для реализации LDAP

Из первой части известно, что Acegi использует для аутентификации APF-фильтр (Authentication Processing Filter –фильтр для выполнения аутентификации). APF-фильтр выполняет все серверные вычислительные задачи, такие как извлечение имени пользователя и пароля из запроса клиента, считывание параметров пользователя из серверного хранилища пользователей и использование этой информации для аутентификации пользователя.

Мы уже настраивали APF-фильтр в первой части, для реализации на основе файла свойств. Теперь база пользователей будет храниться в каталоге LDAP, так что потребуется настроить фильтр по-другому, для взаимодействия с LDAP-каталогом. Начнем с листинга 2, где показано, как был настроен APF-фильтр для реализации на основе файла свойств (properties) в разделе Фильтр для выполнения аутентификации в прошлой статье:

Листинг 2. Настройка APF-фильтра на использование файла properties
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">

<property name="authenticationManager" ref="authenticationManager" />

<property name="authenticationFailureUrl"
value="/login.jsp?login_error=1" />

<property name="defaultTargetUrl"
value="/index.jsp" />

<property name="filterProcessesUrl"
value="/j_acegi_security_check" />

</bean>

Если посмотреть на листинг 2, то видно, что в APF-фильтр передаются четыре параметра. Для хранения в LDAP–сервере необходимо изменить только первый параметр (менеджер аутентификации - authenticationManager). Три других параметра остаются без изменений.

Настройка менеджера аутентификации

В листинге 3 показано, как настроить менеджер аутентификации Acegi для взаимодействия с LDAP-сервером:

Листинг 3. Настройка менеджера аутентификации Acegi для взаимодействия с LDAP
<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">

<property name="providers">
<list>
<ref local="ldapAuthenticationProvider" />
</list>
</property>

</bean>

В листинге 3 процессом аутентификации в Acegi управляет класс менеджера org.acegisecurity.providers.ProviderManager. Для выполнения своей работы менеджеру аутентификации требуются один или несколько провайдеров аутентификации. Для настройки одного или нескольких провайдеров можно воспользоваться параметром providers bean-компонента менеджера. В листинге 3 показан только один провайдер – провайдер для LDAP-аутентификации.

Провайдер для LDAP-аутентификации берет на себя все взаимодействие с используемым каталогом LDAP. Его также необходимо настроить, как будет показано ниже.

Настройка провайдера для LDAP-аутентификации

В листинге 4 приведена конфигурация провайдера для LDAP-аутентификации:

Листинг 4. Настройка провайдера для LDAP-аутентификации
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">

<constructor-arg><ref local="authenticator"/></constructor-arg>

<constructor-arg><ref local="populator"/></constructor-arg>

</bean>

Отметим, что название класса провайдера для LDAP-аутентификации - org.acegisecurity.providers.ldap.LdapAuthenticationProvider. В его конструктор передаются два параметра в виде двух тегов <constructor-arg>, как показано в листинге 4.

Первый параметра конструктора класса LdapAuthenticationProvider - это authenticator (аутентификатор), который аутентифицирует пользователя в LDAP-каталоге путем проверки имени и пароля пользователя. После того как пользователь был аутентифицирован, второй параметр populator (обработчик) извлекает информацию о правах доступа пользователя или его бизнес-ролях из LDAP-каталога.

В следующих разделах будет показано, как настроить bean-компоненты authenticator и populator.


Настройка bean-компонента authenticator

Bean-компонент authenticator проверяет наличие пользователя с указанным именам и паролем в LDAP-каталоге. Acegi предоставляет для этого компонента класс org.acegisecurity.providers.ldap.authenticator.BindAuthenticator, который выполняет требуемую функцию проверки имени и пароля пользователя.

Конфигурация для bean-компонента authenticator показана в листинге 5:

Листинг 5. Конфигурация bean-компонента authenticator
<bean id="authenticator"
class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">

<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>

<property name="userDnPatterns">
<list>
<value>uid={0},ou=employees,ou=partners</value>
<value>uid={0},ou=customers,ou=partners</value>
<value>uid={0},ou=suppliers,ou=partners</value>
</list>
</property>

<property name="userSearch"><ref local="userSearch"/></property>

</bean>

В листинге 5 конструктор класса BindAuthenticator принимает один параметр в виде тега <constructor-arg>. Название этого параметра в листинге 5 - initialDirContextFactory. Этот параметр на самом деле – другой bean-компонент, конфигурация которого будет обсуждаться позже.

Сейчас достаточно просто знать, что назначение bean-компонента initialDirContextFactory – определить исходный контекст для дальнейших операций поиска. Исходный контекст – это DN, задающее определенный элемент в каталоге LDAP. После задания исходного контекста все дальнейшие операции по поиску, например, поиск конкретного пользователя, выполняются в потомках этого элемента.

Например, вернемся к элементу partners на рисунке 2, DN которого - ou=partners,o=manufacturingEnterprise,dc=org. Если указать элемент partners в качестве исходного контекста, то Acegi будет искать пользователей только внутри потомков элемента partners.

Указание DN-шаблонов

В дополнение к настройке конструктора BindAuthenticator необходимо также настроить два параметра bean-компонента authenticator (два тега <property> в листинге 5).

Первый тег <property> определяет свойство userDnPatterns, содержащее один или несколько DN-шаблонов. DN-шаблон определяет несколько LDAP-элементов, у которых есть что-то общее, например, группу всех потомков элемента employees на рисунке 2.

Bean-компонент authenticator Acegi собирает один DN из каждого DN-шаблона, настроенного в свойстве userDnPatterns этого bean-компонента. Например, первый DN-шаблон, настроенный в листинге 5, - uid={0},ou=employees,ou=partners. Bean-компонент authenticator заменяет {0} на имя, указанное пользователем во время аутентификации, например, alice. После замены {0} на имя пользователя DN-шаблон превращается в относительное DN (relative DN - RDN), uid=alice,ou=employees,ou=partners, которому требуется исходный контекст, чтобы стать полноценным DN.

Например, рассмотрим элемент alice на рисунке 2. Этот элемент является первым потомком элемента employees. Его DN: uid=alice,ou=employees,ou=partners,o=manufacturingEnterprise, dc=org. Если использовать строку o=manufacturingEnterprise,dc=org как исходный контекст и применить её после относительного DN uid=alice,ou=employees,ou=partners, то мы получим полный DN для alice.

После создания подобным способом DN пользователя из шаблона DN bean-компонент authenticator отправляет DN и пароль пользователя в LDAP-каталог. Каталог проверяет, что существует подобный DN с указанным паролем. Если проверка проходит успешно, то пользователь аутентифицируется. Этот процесс в терминологии LDAP называется присваивающей аутентификацией. LDAP предлагает и другие механизмы для аутентификации, но в примерах будет использоваться только присваивающая аутентификация.

Если DN, созданное с помощью первого DN-шаблона, не обнаруживается в каталоге, то bean-компонент authenticator пробует следующий DN-шаблон, настроенный в списке. Таким способом bean-компонент authenticator пробует все DN-шаблоны для создания правильного DN пользователя, которого необходимо аутентифицировать.

Поисковые фильтры

В предыдущем разделе "Настройка каталога LDAP" упоминалось, что я разрешил определенную гибкость хранения информации о пользователях в каталоге LDAP. Для этого был создан специальной пользователь specialUser в элементе departments, как было показано на рисунке 1.

Если попробовать создать DN для специального пользователя, используя все DN-шаблоны, сконфигурированные в листинге 5, то выяснится, что для этого не годится ни один из шаблонов. В результате, когда этот пользователь попробует войти в систему, bean-компонент authenticator не сможет сконструировать правильное DN, а, следовательно, и аутентифицировать пользователя.

Acegi обрабатывает подобные исключительные случаи, разрешая определять поисковые фильтры. Bean-компонент authenticator с помощью поисковых фильтров находит пользователей, которых не удалось аутентифицировать, создав DN из DN-шаблонов.

Второй тег <property> в листинге 5 имеет вложенный тег <ref>, ссылающийся на bean-компонент userSearch. Этот bean-компонент userSearch определяет поисковый запрос. В листинге 6 показано, как настроить bean-компонент userSearch для обработки специальных пользователей:

Листинг 6. Настройка поискового запроса для поиска специальных пользователей
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">

<constructor-arg>
<value>ou=departments</value>
</constructor-arg>

<constructor-arg>
<value>(uid={0})</value>
</constructor-arg>

<constructor-arg>
<ref local="initialDirContextFactory" />
</constructor-arg>

<property name="searchSubtree">
<value>true</value>
</property>

</bean>

Параметры поискового запроса

В листинге 6 показано, что bean-компонент userSearch – это объект класса org.acegisecurity.ldap.search.FilterBasedLdapUserSearch, конструктор которого принимает три параметра. Первый параметр определяет элемент, где bean-компонент authenticator выполняет поиск пользователей. Значение первого параметра - ou=departments, это относительное DN, определяющее элемент departments, показанный на рисунке 2.

Второй параметр (uid={0}) определяет поисковый фильтр. Так как мы используем для определения пользователей атрибут uid, то можно найти пользователя, ища элемент, у которого атрибут uid имеет определенное значение. Как можно предположить, ноль в фигурных скобках просто говорит Acegi, что необходимо заменить {0} на имя аутентифицируемого пользователя, в данном случае specialUser.

Третий параметр – это ссылка на исходный контекст, которая уже обсуждалась при разборе конструктора класса BindAuthenticator в листинге 5. С учетом этого, после задания исходного контекста, все дальнейшие операции поиска происходят внутри потомков элемента, на который указывает исходный контекст. Обратите внимание, что относительное DN, указанное в виде значения первого параметра в листинге 6 (ou=departments), помещается перед исходным контекстом.

В дополнение к этим трем параметрам конструктора bean-компонент userSearch из листинге 6 также использует параметр searchSubtree. Если значение этого параметра установлено в true, то операция поиска проводится по поддереву (все потомки, потомки потомков и т.д.) элемента, который был указан в качестве значения первого параметра конструктора.

На этом конфигурация bean-компонента authenticator завершена. В следующем разделе разбирается конфигурация bean-компонента populator, также показанного в листинге 4.


Настройка bean-компонента populator

Bean-компонент populator считывает бизнес-роли пользователя, уже аутентифицированного bean-компонентом authenticator. В листинге 7 приведена XML конфигурация для bean-компонента populator:

Листинг 7. XML конфигурация bean-компонента populator
<bean id="populator"
class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">

<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>

<constructor-arg>
<value>ou=departments</value>
</constructor-arg>

<property name="groupRoleAttribute">
<value>ou</value>
</property>

<property name="searchSubtree">
<value>true</value>
</property>

</bean>

В листинге 7 конструктор bean-компонента populator принимает два аргумента и параметр groupRoleAttribute. Первый параметр конструктора определяет исходный контекст, используемый bean-компонентом для считывания бизнес-ролей аутентифицированного пользователя. Не обязательно использовать один и тот исходный контекст для обоих bean- компонентов - authenticator и populator. Можно настроить отдельные исходные контексты для каждого компонента.

Второй параметр конструктора определяет относительное DN, которое bean-компонент populator добавляет перед исходным контекстом. Таким способом относительное DN формирует DN элемента, содержащего группу пользователей, например, элемента departments.

Параметр groupRoleAttribute bean-компонента populator определяет атрибут, хранящий информацию о бизнес-ролях участников группы. Если вернуться к разделу Настройка LDAP-каталога, то информация о бизнес-ролях каждой группы хранится в атрибуте ou. Поэтому ou указано как значение параметра groupRoleAttribute, как показано в листинге 7.

Как можно предположить, bean-компонент populator ищет в LDAP-каталоге элементы групп, к которым принадлежит аутентифицированный пользователь. Затем он считывает значения, связанные с атрибутом ou элементов групп, чтобы узнать бизнес-роли аутентифицированного пользователя.

На этом конфигурация bean-компонента populator завершена. На данный момент исходный контекст уже используется в трех местах: в листинге 5, листинге 6 и листинге 7. Так что пришло время настроить исходный контекст.

Конфигурирование исходного контекста

В листинге 8 показано, как задать исходный контекст в Acegi:

Листинг 8. XML-конфигурация исходного контекста
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">

<constructor-arg value="ldap://localhost:389/o=manufacturingEnterprise,dc=org"/>

<property name="managerDn">
<value>cn=manager,o=manufacturingEnterprise,dc=org</value>
</property>

<property name="managerPassword">
<value>secret</value>
</property>

</bean>

Название класса исходного контекста в Acegi в листинге 8: org.acegisecurity.ldap.DefaultInitialDirContextFactory. Это класс-фабрика, входящий в состав Acegi. Acegi внутри себя использует этот класс для создания объектов других классов, которые выполняют операции в каталоге, такие как поиск. При настройке класса-фабрики для исходного контекста необходимо указать следующие параметры:

  • Сетевой адрес LDAP-каталога и корневой элемент каталога в качестве аргумента конструктора. Элемент, настроенный в исходном контексте, будет использоваться как корневой элемент. Это значит, что все последующие операции, такие как поиск (search), будут выполняться в поддереве, определенном корневым элементом.
  • DN и пароль, заданные соответственно, в виде параметров managerDn и managerPassword. Acegi необходимы DN и пароль, чтобы иметь возможность аутентифицироваться на сервере каталогов, прежде чем выполнять любые операции поиска.

Мы уже разобрали, как хранить базу пользователей в каталоге LDAP и как настроить Acegi на использование информации из LDAP для аутентификации пользователей. В следующем разделе детально разбирается фильтр Acegi для выполнения аутентификации (Authentication Processing Filter – APF), чтобы продемонстрировать, как сконфигурированные bean-компоненты управляют процессом аутентификации.


Аутентификация и авторизация

Теперь, когда APF-фильтр настроен, пришло время начать взаимодействовать с каталогом LDAP для аутентификации пользователей. Некоторые действия, выполняемые APF в процессе общения с каталогом, могут показаться знакомыми по первой части, где разбиралось, как фильтр работает с различными службами при аутентификации пользователя. Диаграмма последовательности действий, изображенная на рисунке 5, очень похожа на диаграмму с рисунка 3 в первой части:

Рисунок 5. APF-фильтр выполняет аутентификацию LDAP-пользователя
Рисунок 5. APF-фильтр выполняет аутентификацию LDAP-пользователя

Шаги с первого по девятый остаются без изменений, вне зависимости от того использует ли APF-фильтр файл properties для внутренней аутентификации или взаимодействует с LDAP-сервером. Первые девять шагов кратко перечисляются в списке, чтобы потом можно было продолжить изучение событий, относящихся непосредственно к LDAP, начиная с десятого шага:

  1. Предыдущий фильтры в цепочке тестирования передает объект: запрос, ответ и цепочку фильтрации в APF-фильтр.
  2. APF-фильтр создает объект-аутентификатор с именем пользователя, паролем и другой информацией, извлеченной из объекта запроса.
  3. APF-фильтр передает объект-аутентификатор менеджеру аутентификации.
  4. Менеджер аутентификации может содержать одного или несколько провайдеров для выполнения аутентификации. Каждый провайдер поддерживает единственный тип аутентификации. Менеджер проверяет, какой из его провайдеров поддерживает объект-аутентификатор, полученный из APF-фильтра.
  5. Менеджер аутентификации передает объект-аутентификатор провайдеру, который подходит для данного типа аутентификации.
  6. Провайдер аутентификации извлекает имя пользователя из объекта-аутентификатора и передает его в службу кэширования пользователей. Acegi поддерживает кэш уже аутентифицированных пользователей. В следующий раз, когда пользователь попробует войти в систему, Acegi сможет загрузить информацию о нем: имя пользователя, пароль и уровни доступа из кэша, вместо того чтобы обращаться к центральному хранилищу пользователей. Такой подход повышает производительность.
  7. Служба кэширования пользователей проверяет, имеется ли информация о пользователе в кэше.
  8. The user cache service returns the details of the user to the authentication provider. If the cache does not contain user details, it returns null.
  9. Провайдер аутентификации проверяет, что было получено из службы кэширования пользователей: информация о пользователе или null.
  10. С этого момента выполнение аутентификации связано с использованием LDAP. Если из кэша был получен null, то провайдер для LDAP-аутентификации передает имя пользователя и пароль, извлеченные на шестом шаге, в bean-компонент authenticator, сконфигурированный в листинге 5.
  11. Bean-компонент authenticator создает несколько вариантов DN пользователя, используя DN-шаблоны, сконфигурированные через свойство userDnPatterns в листинге 5. Он пробует все доступные DN-шаблоны один за другим, создавая DN на основе этого DN-шаблона и отправляя его вместе с паролем пользователя, извлеченным из запроса пользователя, в LDAP-каталог. LDAP-каталог проверяет, существует ли такое DN, и правильно ли указан пароль. Если один из DN-шаблонов срабатывает, то пользователь считается привязанным к LDAP-каталогу, и authenticator переходит к шагу 15.
  12. Если ни один из DN-шаблонов не срабатывает, это значит, что пользователя с таким именем и паролем не существует ни в одном из мест, определенных DN-шаблонами. Тогда bean-компонент authenticator выполняет поиск пользователя в LDAP-каталоге согласно поисковому запросу, сконфигурированному в листинге 6. Если LDAP-каталог не может обнаружить пользователя, то аутентификация считается неудачной
  13. Если LDAP-каталог находит пользователя, он возвращает DN пользователя обратно в bean-компонент authenticator.
  14. Bean-компонент authenticator отправляет DN пользователя и пароль в LDAP-каталог, чтобы проверить, что был введен правильный пароль. Если LDAP-каталог находит, что пароль был указан правильно, то пользователь считается привязанным к LDAP-каталогу.
  15. Bean-компонент authenticator отправляет информацию о пользователе обратно в провайдер для LDAP-аутентификации.
  16. Провайдер для LDAP-аутентификации передает управление bean-компоненту populator.
  17. Bean-компонент populator ищет группы, к которым принадлежит пользователь.
  18. LDAP-каталог возвращает информацию о ролях пользователя в bean-компонент populator.
  19. Bean-компонент populator возвращает информацию о ролях провайдеру для LDAP-аутентификации.
  20. Провайдер для LDAP-аутентификации возвращает информацию о пользователе вместе с информацией о его бизнес-ролях в APF-фильтр. После этого считается, что пользователь был успешно аутентифицирован.

Последние три шага (21, 22 и23) не зависят от метода аутентификации.

Настройка перехватывающего фильтра

Мы разобрали действия APF-фильтра для авторизации пользователя. Далее необходимо проверить, действительно ли успешно аутентифицированный пользователь авторизован для доступа к запрашиваемому ресурсу. Это задача перехватывающего фильтра Acegi (Interceptor Filter -IF). В этом разделе рассматривается, как настроить IF-фильтр для реализации политики управления доступом.

Вспомним, как мы настраивали IF-фильтр в листинге 7 первой части. Перехватывающий фильтр связывает ресурсы с ролями, гарантируя, что только пользователь, имеющий требуемую роль, может получить доступ к определенному ресурсу. Для демонстрации бизнес-ролей различных отделов нашего предприятия в листинге 9 в существующую конфигурацию IF-фильтра добавляется еще одна роль:

Листинг 9. Конфигурация перехватывающего фильтра
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">

<property name="authenticationManager" ref="authenticationManager" />

<property name="accessDecisionManager" ref="accessDecisionManager" />

<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/protected/engineering/**=ROLE_HEAD_OF_ENGINEERING
/protected/marketing/**=ROLE_HEAD_OF_MARKETING
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>

</bean>

В листинге 9 IF-фильтр принимает три параметра. Первый и третий параметры остались такими же, как были сконфигурированы изначально в первой части. Однако добавлен второй параметр - bean-компонент accessDecisionManager.

Bean-компонент accessDecisionManager отвечает за принятие решения по авторизации. Он использует определения значения для управления доступом, указанные в третьем параметре в листинге 9 для принятия решений по авторизации (управлению доступом). Третий параметр – это objectDefinitionSource.

Настройка bean-компонента accessDecisionManager

Bean-компонент accessDecisionManager определяет, может ли пользователь получить доступ к ресурсу. В Acegi представлено несколько подобных компонентов – менеджеров для принятия решений о доступе, которые отличаются механизмом, используемым ими для принятия подобных решений. В данной статье рассматривает только один тип менеджера для управления доступом, который конфигурируется в листинге 10:

Листинг 10. Конфигурация менеджера для управления доступом
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">

<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter" />
</list>
</property>

</bean>

В листинге 10 bean-компонент accessDecisionManager – это объект класса org.acegisecurity.vote.AffirmativeBased. Bean-компонент accessDecisionManager принимает только один параметр – список компонентов-избирателей (voters).

В Acegi компонент-избиратель определяет (голосует), имеет ли пользователь права на доступ к указанному ресурсу. При запросе от bean-компонента accessDecisionManager у компонента-избирателя есть один из трех вариантов ответа: доступ подтвержден (access-granted); в доступе отказано (access-denied); воздержаться от голосования, если не уверен.

Различные типы менеджеров для управления доступом отличаются тем, как они трактуют решения компонентов-избирателей. Менеджер для принятия решений AffirmativeBased, показанный в листинге 10, реализует простую логику решения: если хотя бы один из компонентов-избирателей проголосовал положительно, то пользователю разрешается доступ к запрошенному ресурсу.

Логика компонентов-избирателей

В Acegi представлено несколько типов реализации компонентов-избирателей. Bean-компонент accessDecisionManager передает информацию об аутентифицированном пользователе, включая его бизнес-роли, и объект objectDefinitionSource в компонент-избиратель. В примере используются компоненты-избиратели двух типов: RoleVoter и AuthenticatedVoter, как показано в листинге 10. Рассмотрим логику каждого из этих компонентов:

  • RoleVoter голосует, только если он смог обнаружить роль, начинающуюся с префикса ROLE_ в строке из объекта objectDefinitionSource. Если компонент-избиратель RoleVoter не смог найти ни одной такой строки, он воздерживается от голосования. Если он находит совпадающую роль в списке бизнес-ролей пользователя, то он подтверждает доступ, а если не находит подобной роли, то отказывает в доступе. В листинге 9 есть две роли с префиксом ROLE_: ROLE_HEAD_OF_ENGINEERING и ROLE_HEAD_OF_MARKETING.
  • AuthenticatedVoter голосует, только если он смог обнаружить в объекте objectDefinitionSource строки с какой-нибудь предопределенной ролью. В листинге 9, есть одна такая строка: IS_AUTHENTICATED_ANONYMOUSLY. Анонимная аутентификация означает, что пользователь не может быть аутентифицирован. Найдя эту строку, AuthenticatedVoter проверяет, могут ли некоторые незащищенные ресурсы (невключенные ни в одну из строк с префиксом ROLE_) быть доступны анонимно аутентифицированному пользователю. Если AuthenticatedVoter обнаруживает, что запрашиваемый ресурс не защищен и объект objectDefinitionSource разрешает доступ к незащищенным ресурсам анонимно аутентифицированным пользователям, то он голосует за подтверждение доступа, в противном случае – за отказ в доступе.

Пример приложения

В этой статье представлен пример приложения, демонстрирующего уже изученные основы Acegi и LDAP. LDAP-Acegi приложение показывает аутентифицированным пользователям главную страницу с инженерными и маркетинговыми документами соответственно. Как будет показано, LDAP-Acegi приложение позволяет пользователю alice просматривать инженерные документы, а пользователю bob – маркетинговые. Также оно позволяет специальному пользователю просматривать документы обоих типов. Все это было настроено в начале статьи при конфигурации сервера каталогов LDAP. Скачайте пример приложения, чтобы исследовать его более подробно.


Заключение

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


Загрузка

ОписаниеИмяРазмер
примеры кода из статьиj-acegi2.zip10KB

Ресурсы

Научиться

Получить продукты и технологии

  • ApacheDS: скачайте с Apache.org.(EN)
  • JXplorer: LDAP-клиент с открытым кодом, написанный на Java и использовавшийся в этой статье.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Open source
ArticleID=311542
ArticleTitle=Обеспечение безопасности Java-приложений с помощью Acegi: Часть 2. Работа с сервером каталогов LDAP
publish-date=06022008