Учебные материалы

Аутентификация пользователя в LDAP

Этот урок освещает процесс создания приложения и его защиту с помощью Spring Security LDAP модуля.

Что вы создадите

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

Что вам потребуется

  • Примерно 15 минут свободного времени
  • Любимый текстовый редактор или IDE
  • JDK 6 и выше
  • Gradle 1.11+ или Maven 3.0+
  • Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.

Как проходить этот урок

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

Чтобы начать с нуля, перейдите в Настройка проекта.

Чтобы пропустить базовые шаги, выполните следующее:

Когда вы закончите, можете сравнить получившийся результат с образцом в gs-authenticating-ldap/complete.

Настройка проекта

Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle и Maven. Если вы не знакомы ни с одним из них, ознакомьтесь с соответсвующими уроками Сборка Java-проекта с использованием Gradle или Сборка Java-проекта с использованием Maven.

Создание структуры каталогов

В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру, командой mkdir -p src/main/java/hello для *nix систем:

└── src
    └── main
        └── java
            └── hello

Создание файла сборки Gradle

Ниже представлен начальный файл сборки Gradle. Файл pom.xml находится здесь. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.

Если вы посмотрите на pom.xml, вы найдете, что указана версия для maven-compiler-plugin. В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы, которая по умолчанию имеет старую(до Java 5) версию этого плагина.

build.gradle

buildscript {
    repositories {
        maven { url "http://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-authenticating-ldap'
    version =  '0.1.0'
}

repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/libs-release" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.11'
}

Spring Boot gradle plugin предоставляет множество удобных возможностей:

  • Он собирает все jar'ы в classpath и собирает единое, исполняемое "über-jar", что делает более удобным выполнение и доставку вашего сервиса
  • Он ищет public static void main() метод, как признак исполняемого класса
  • Он предоставляет встроенное разрешение зависимостей, с определенными номерами версий для соответсвующих Spring Boot зависимостей. Вы можете переопределить на любые версии, какие захотите, но он будет по умолчанию для Boot выбранным набором версий

Создание простого web-контроллера

В Spring, конечной точкой web-обработки являются простые Spring MVC контроллеры. Ниже представлен Spring MVC контроллер, который обрабатывает GET / запрос и возвращает простое сообщение:

src/main/java/hello/HomeController.java

package hello;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController {

    @RequestMapping("/")
    public @ResponseBody String index() {
        return "Welcome to the home page!";
    }
}

Весь класс отмечен @Controller, поэтому Spring MVC может обнаружить контроллер, используя встроенные возможности сканирования и настройки web-маршрутов.

Метод помечен как @RequestMapping, обозначающий путь и REST действие. В данном случае, GET является поведением по умолчанию; он возвращает сообщение о том, что вы на домашней странице.

@ResponseBody сообщает Spring MVC писать текст напрямую в тело HTTP ответа, поэтому здесь нет представлений. Вместо этого, когда вы переходите на страницу, вы получите в браузере сообщение о том, что данный урок имеет LDAP защиту.

Создание незащищенного web-приложения

До того, как вы защитите web-приложение, проверьте, что оно работает. Для этого вам необходимо определить несколько ключевых бинов. Для этого создайте Application класс.

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Сборка исполняемого JAR

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

./gradlew build

Затем вы можете запустить JAR-файл:

java -jar build/libs/gs-authenticating-ldap-0.1.0.jar

Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run, либо вы можете собрать приложение с mvn clean package и запустить JAR примерно так:

java -jar target/gs-authenticating-ldap-0.1.0.jar
Процедура, описанная выше, создает исполняемый JAR. Вы также можете вместо него собрать классический WAR-файл.

Запуск незащищенного web-приложения

Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:

./gradlew clean build && java -jar build/libs/gs-authenticating-ldap-0.1.0.jar
Если вы используете Maven, то можете запустить ваш сервис таким образом: mvn clean package && java -jar target/gs-authenticating-ldap-0.1.0.jar.

Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:

./gradlew bootRun
С mvn - mvn spring-boot:run.

Когда сервер запустится, вам необходимо будет открыть браузер и зайти на http://localhost:8080, вы должны увидеть следующий текст:

Welcome to the home page!

Установка Spring Security

Для настройки Spring Security, вам для начала нужно добавить некоторые дополнительные зависимости в вашу сборку.

Для сборки на основе Gradle:

build.gradle

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.security:spring-security-ldap:3.2.4.RELEASE")
    compile("org.apache.directory.server:apacheds-server-jndi:1.5.5")
    testCompile("junit:junit")
}

Для сборки на основе Maven:

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-ldap</artifactId>
        <version>3.2.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.directory.server</groupId>
        <artifactId>apacheds-server-jndi</artifactId>
        <version>1.5.5</version>
    </dependency>
</dependencies>

Эти зависимости добавляют Spring Security и ApacheDS, LDAP сервер с открытым исходным кодом. С этого момента вы можете использовать Java для настройки вашей политики безопасности.

src/main/java/hello/WebSecurityConfig.java

package hello;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

	@Configuration
	protected static class AuthenticationConfiguration extends
			GlobalAuthenticationConfigurerAdapter {

		@Override
		public void init(AuthenticationManagerBuilder auth) throws Exception {
			auth.ldapAuthentication().userDnPatterns("uid={0},ou=people")
					.groupSearchBase("ou=groups").contextSource()
					.ldif("classpath:test-server.ldif");
		}
	}
}

@EnableWebSecurity подключает необходимые бины для использования Spring Security.

Вам также нужен LDAP сервер. Spring Security LDAP модуль подключает встроенный сервер, написанный на чистой Java, которая используется в этом уроке. Метод ldapAuthentication() настроен так, что имя пользователя из формы входа помещается в {0}, таким образом осуществляется поиск uid={0},ou=people,dc=springframework,dc=org на LDAP сервере.

Установка пользовательских данных

LDAP сервера могут использовать LDIF (LDAP Data Interchange Format) файлы для обмена пользовательскими данными. Метод ldif() внутри WebSecurityConfig подтягивает файл данных LDIF. Это делается для упрощения предзагрузки демонстрационных данных.

src/main/resources/test-server.ldif

dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: subgroups

dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people

dn: ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: space cadets

dn: ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: "quoted people"

dn: ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: otherpeople

dn: uid=ben,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=

dn: uid=bob,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: bobspassword

dn: uid=joe,ou=otherpeople,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Joe Smeth
sn: Smeth
uid: joe
userPassword: joespassword

dn: cn=mouse\, jerry,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Mouse, Jerry
sn: Mouse
uid: jerry
userPassword: jerryspassword

dn: cn=slash/guy,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: slash/guy
sn: Slash
uid: slashguy
userPassword: slashguyspassword

dn: cn=quote\"guy,ou=\"quoted people\",dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: quote\"guy
sn: Quote
uid: quoteguy
userPassword: quoteguyspassword

dn: uid=space cadet,ou=space cadets,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Space Cadet
sn: Cadet
uid: space cadet
userPassword: spacecadetspassword



dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: developers
ou: developer
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: uid=bob,ou=people,dc=springframework,dc=org

dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: managers
ou: manager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
uniqueMember: cn=mouse\, jerry,ou=people,dc=springframework,dc=org

dn: cn=submanagers,ou=subgroups,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: submanagers
ou: submanager
uniqueMember: uid=ben,ou=people,dc=springframework,dc=org
Использование LDIF файла не является стандартной практикой для реальных систем. Однако, это очень полезно для тестирования каких-то задач и уроков.

Создание класса Application

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Метод main() передает управление вспомогательному классу SpringApplication, предоставляя Application.class как аргумент его run() методу. Это говорит Spring о том, чтобы прочитать аннотацию метаданных из Application и управлять им как компонентом в Spring Application Context.

Аннотация @ComponentScan сообщает Spring о запуске рекурсивного поиска в пакете hello и потомках классов, отмеченных прямо или косвенно Spring аннотацией @Component. При этом гарантируется, что Spring найдет и зарегистрирует WebSecurityConfig, потому что он отмечен @Configuration, что в свою очередь является своего рода @Component аннотацией.

@EnableAutoConfiguration аннотация переключает на доступные по умолчанию настройки, основанные на содержимом вашего classpath. К примеру, она смотрит на любой класс, который реализует CommandLineRunner интерфейс и вызывает его метод run(). В данном случае это демо-код этого урока.

Сборка исполняемого JAR

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

./gradlew build

Затем вы можете запустить JAR-файл:

java -jar build/libs/gs-authenticating-ldap-0.1.0.jar

Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run, либо вы можете собрать приложение с mvn clean package и запустить JAR примерно так:

java -jar target/gs-authenticating-ldap-0.1.0.jar
Процедура, описанная выше, создает исполняемый JAR. Вы также можете вместо него собрать классический WAR-файл.

Запус защищенного web-приложения

Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:

./gradlew clean build && java -jar build/libs/gs-authenticating-ldap-0.1.0.jar
Если вы используете Maven, то можете запустить ваш сервис таким образом: mvn clean package && java -jar target/gs-authenticating-ldap-0.1.0.jar.

Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:

./gradlew bootRun
С mvn - mvn spring-boot:run.

Этим запускается серверная часть для получения загружаемого файла. Отладочные данные отображены. Сервис должен быть поднят и запущен через несколько секунд.

Когда сервер запустится, вам необходимо будет открыть браузер и зайти на http://localhost:8080, вы должны быть перенаправлены на страницу входа, предоставленную Spring Security.

Введите имя пользователя ben и пароль benspassword. Вы должны увидеть следующее сообщение:

Welcome to the home page!

Итог

Поздравляем! Вы только что написали web-приложение и защитили его Spring Security. В данном случае вы использовали хранилище пользовательских данных на основе LDAP.

С оригинальным текстом урока вы можете ознакомиться на spring.io.

comments powered by Disqus