LDAP заявки с Spring Boot LdapTemplate

Наскоро си играех с Active Directory (AD) и се опитах да извлека потребителска информация чрез LDAP заявки. Това определено беше разочароващо преживяване, тъй като не бях много запознат с LDAP. Затова реших да пиша за това, което научих.

Забележка: в момента на писане работя с Spring Boot v2.7.7, Gradle v7.6, Docker-Compose v2.14.0, Docker v20.10.3 и Java v11.



Преглед

Нека започнем с малко фонов контекст. Active Directory (AD) е често използвана директорийна услуга от много компании за управление на потребители и групи, администриране на политики, удостоверяване и т.н. LDAP (Lightweight Directory Access Protocol) е протокол, който можем да използваме за комуникация с LDAP сървърите ( напр. рекламата).

Когато разработваме услуги, можем да удостоверяваме потребителите с LDAP или да извличаме потребителска информация от LDAP сървъра. В тази статия ще се съсредоточа основно върху някои от методите, които използвах за запитване до LDAP сървъра с помощта на LdapTemplate. LdapTemplate е набор от готови за използване API за изпълнение на основни LDAP операции като създаване, модифициране, извличане и т.н.

Настройка на LDAP сървър

Преди да започнем, ще трябва да имаме LDAP сървър, за да правим заявки. В тази демонстрация, вместо да настроим цяла Active Directory, ще настроим LDAP сървър с помощта на osixia/docker-openldap. Това е OpenLDAP докер изображение, което ни позволява лесно да попълваме LDAP базата данни с помощта на LDIF файл.

За да започнем, ще подготвим LDIF файл за стартиране, който съдържа всички данни, с които искаме да заредим LDAP базата данни. За демонстрационни цели ние ще попълним само LDAP базата данни с потребителска информация, както е показано по-долу.

Има 3 потребители (известни също като запис), попълнени в LDAP базата данни с 3 атрибута, които ще извлечем в нашата демонстрация:

  • uid : съхранява идентификатора на потребителя
  • displayname : съхранява името на потребителя
  • mail : съхранява всички имейл адреси на потребителя

Забележка: LDAP запис е колекция от информация за обект. Всеки запис се състои от три основни компонента: отличително име, колекция от атрибути и колекция от класове обекти

За да стартирам OpenLdap сървъра, създадох следния YAML файл за съставяне на докери.

Обърнете внимание, че домейнът е example.org и LDAP директорията има следната дървовидна структура след зареждане с файла LDIF, дефиниран по-горе.

Запитване с ldapsearch

Когато OpenLdap сървърът работи, можем да опитаме да изработим някои LDAP заявки, използвайки ldapsearch, както е показано по-долу.

# Step into the OpenLdap Docker Container Instance
docker exec -it openldap /bin/bash

# Execute LDAP query
ldapsearch -x \
    -H ldap://localhost:389 \
    -b "dc=example,dc=org" \
    -D "cn=admin,dc=example,dc=org" \
    -w admin

Опцията -b показва базата за търсене за заявката и в нашия случай можем да използваме dc=example,dc=org или ou=Users,dc=example,dc=org, както е посочено в дървовидната структура на нашата LDAP директория по-горе.

Опцията -D и -w показва кое отличително име за обвързване е необходимо за удостоверяване и по подразбиране docker-openldap създава администраторски акаунт с парола admin.

Забележка: Отличителното име (DN) е името, което уникално идентифицира и описва запис в LDAP сървър.

След като вече знаем как да правим заявки с помощта на ldapsearch, нека се опитаме да направим същото в Spring Boot с LdapTemplate.

Създайте Spring Boot Project

Spring Boot предоставя Spring Data LDAP, която е библиотека за просто LDAP програмиране. Това ни позволява лесно да конфигурираме Spring Boot проект за свързване и комуникация с LDAP сървър. Можем да направим това, като импортираме зависимостта spring-boot-starter-data-ldap към нашия проект.

# Gradle
dependencies {
 implementation("org.springframework.boot:spring-boot-starter-data-ldap")
}
# Maven
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

След това, за да се свържете със сървъра OpenLdap, ще предоставим следните настройки за връзка в нашия application.yaml:

spring.ldap:
  urls: ldap://localhost:389
  base: dc=example,dc=org
  username: cn=admin,dc=example,dc=org
  password: admin

С това вече сме готови да използваме LdapTemplate за запитване до OpenLdap сървъра.

Запитване с LdapTemplate

Първо, нека проверим всеки потребителски запис. По-долу е даден пример за потребителски запис.

dn: cn=Amanda,ou=Users,dc=example,dc=org
objectclass: inetOrgPerson
uid: U0
cn: Amanda
sn: Amanda
givenname: Amanda
displayname: Amanda
mail: [email protected]
mail: [email protected]
userpassword: amanda

Интересуваме се да извлечем атрибутите uid, displayname и mail. Както може би сте забелязали, има повече от 1 стойност за атрибута mail. Така че нека да видим как можем да извлечем всички тези атрибути с помощта на LdapTemplate.

#1 Използване на AttributesMapper

Когато търсим с LdapTemplate, можем да използваме AttributesMapper за картографиране на стойностите на атрибута. Според „документите“,

Вътрешно LdapTemplate итерира всички намерени записи, извиквайки дадения AttributesMapper за всеки запис и събира резултатите в списък. След това списъкът се връща от метода за търсене.

Ето пример за това как можем да извлечем атрибутите на потребителя с помощта на AttributesMapper. Забележете, че търсим от директорията ou=Users в основната директория dc=example,dc=org, която дефинирахме по-горе. Филтър uid=$userId също се прилага за намиране на конкретния потребителски запис.

Очакван резултат:

[{
  "userId":"U0",
  "name":"Amanda",
  "emails":["[email protected]","[email protected]"]
}]

Имайте предвид обаче, че има малко ограничение, при което, ако се опитвате да извлечете атрибути, които не съществуват, ще получите NullPointerException. Освен това няма да можете да извлечете отличителното име (dn) или схеми с помощта на AttributesMapper.

#2 Използване на ContextMapper

По подобен начин можем да използваме AbstractContextMapper за картографиране на атрибутите на запис. Според „документите“,

Всеки път, когато бъде намерен запис в дървото на LDAP, неговите атрибути и отличително име (DN) ще бъдат използвани от Spring LDAP за конструиране на DirContextAdapter. Това ни позволява да използваме ContextMapper за трансформиране на намерените стойности.

Ето пример за това как можем да извлечем атрибутите на потребителя с помощта на AbstractContextMapper. Операцията за търсене е същата като AttributesMapper, с изключение на това, че картографът е заменен с AbstractContextmapper.

Очакван резултат:

[{
  "userId":"U0",
  "name":"Amanda",
  "emails":["[email protected]","[email protected]"]
}]

Предимството на ContextMapper е, че

  • той обработва NullPointerException като връща null вместо да хвърля изключения.
  • той опростява операциите за извличане на атрибути (особено за атрибути с много стойности) с getStringAttribute() и getStringAttributes() методи.
  • позволява ви да извличате разграничително име (dn), схеми и класове обекти.

#3 Допълнителни съвети — DefaultIncrementAttributesMapper

Ако знаете отличителното име (dn), можете да използвате DefaultIncrementAttributesMapper, за да търсите многостойностни атрибути (напр. атрибута mail). Ето пример как можете да го използвате.

Очакван резултат

["[email protected]","[email protected]"]

Имайте предвид, че dn на потребителя е cn=Amanda,ou=Users,dc=example,dc=org. Тъй като вече сме дефинирали основната директория да бъде dc=example,dc=org, няма нужда да включвате dc=example,dc=org в dn при търсене. Това ще доведе до код за грешка 32, ако е включен.

javax.naming.NameNotFoundException: [LDAP: error code 32 - No Such Object]; remaining name 'cn=Amanda,ou=Users,dc=example,dc=org'

Резюме

Това е! Ако сте запознати с LDAP и Spring Boot, по-голямата част от тази информация може лесно да бъде намерена в съответната документация. Тази статия има за цел да даде обща представа за това как можете да извършите търсене на атрибути с помощта на LdapTemplate. Надявам се тази статия да ви хареса толкова, колкото ми беше приятно да я напиша :)

За повече информация вижте тези полезни връзки:

Благодаря за четенето.