Статьи

Spring Security с использованием аутентификации API

Фон

Несмотря на то, что во многих блогах подробно рассказывается, как использовать Spring Security, мне все еще сложно настроить, когда проблемный домен находится за пределами стандартной аутентификации LDAP или базы данных. В этой статье я опишу некоторые простые настройки Spring Security, которые позволяют использовать его с вызовом API на основе REST. В частности, в этом случае используется служба API, которая возвращает объект пользователя, который содержит хэш пароля SHA-256.

Настроить

Предварительными условиями для запуска этого образца являются Git и Maven, а также выбранный вами IDE (протестирован с Eclipse и IntelliJ).

Исходный код можно найти по адресу: https://github.com/dajevu/Spring3SecurityUsingAPI . После снятия кода выполните следующие действия:

  1. В окне терминала перейдите в общий каталог, расположенный под корнем, в котором находится исходный код.
  2. Выполните команду mvn clean install. Это создаст подпроект Shared и установит jar в ваш локальный репозиторий mvn.
  3. В Eclipse или IntelliJ импортируйте проект как проект Maven. В Eclipse это приведет к созданию 3 проектов: Shared, SpringWebApp и RestfulAPI. В IntelliJ это будет представлено как подпроекты. После завершения процесса компиляции ошибок быть не должно.
  4. Измените каталог на RestfulAPI. Затем введите команду mvn jetty: run для запуска веб-приложения API. Затем можно выдать следующий URL-адрес, который вернет объект User, представленный в JSON: http: // localhost: 9090 / RestfulAPI / api / v1 / user / john
  5. Откройте новое окно терминала, перейдите в каталог SpringWebApp, расположенный в корневом каталоге проекта. Выполните команду mvn jetty: run. Это запустит стандартное веб-приложение Spring, которое включает Spring Security. Вы можете получить доступ к одной HTML-странице по адресу: http: // localhost: 8080 / SpringWebApp /. После нажатия на ссылку «Войти» войдите под именем пользователя john и паролем doe. Вы должны быть перенаправлены на страницу Hello Admin.

    Для демонстрации решения используются три модуля maven, которые показаны ниже:

    • SpringWebApp . Это типичное весеннее веб-приложение, которое обслуживает одну страницу JSP. Содержание страницы будет зависеть от того, вошел ли пользователь в данный момент или нет. При первом посещении страницы появится ссылка для входа в систему, которая направит их во встроенную форму входа в Spring Security. При попытке входа в систему клиент R ESTEasy используется для вызова службы API (описанной ниже), которая возвращает строку JSON, которая преобразуется в объект Java через клиент RESTEasy. Подробности настройки Spring Security обсуждаются в следующих разделах.
    • RestfulAPI . Служба API, которая обслуживает запросы JSON. Он сконфигурирован с использованием RESTEasy (реализация JAX-RS) и более подробно описан в следующем разделе.
    • Общий . Он содержит несколько классов Java, которые совместно используются двумя другими проектами. В частности, объект User DTO и определение прокси-сервера RESTEasy (оно является общим, поскольку оно также может использоваться клиентом RESTEasy).

    RestfulAPI Dissection

    Веб-приложение API настраивается с использованием реализации RESTEasy Spring. Документация RESTEasy очень тщательна, поэтому я не буду вдаваться в подробное объяснение ее настройки. Определен один вызов API (в UserProxy в общем проекте), который возвращает статическую строку JSON. Прокси-интерфейс API (или интерфейс) определяется следующим образом:

    Resteasy API Proxy

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @Path(UserProxy.Urls.BASE_URL)
    public interface UserProxy {
      
        public interface Urls {
            public static final String BASE_URL = "/api/v1";
            public static final String USER = "/user/{username}";
        }
      
        @GET
        @Produces( { MediaType.APPLICATION_JSON })
        @Path(UserProxy.Urls.USER)
        public User getUserByUsername(@PathParam("username") String username);
    }

    Для тех из вас, кто знаком с JAX-RS, вы легко последуете этой конфигурации. Он определяет URI API, который будет отвечать на запросы, отправленные на URL-путь / api / v1 / user / {username}, где {username} заменяется фактическим значением имени пользователя. Реализация этого сервиса, который просто возвращает статический ответ, показана ниже:

    Единственно удаленно сложным является использование хеширования SHA-256 пароля пользователя. Вскоре мы увидим, как это интерпретируется Spring Security. При обращении к URL возвращается следующая строка JSON:

    Файл web.xml веб-приложения содержит конфигурацию установки для обслуживания запросов RESTEasy, поэтому, если вам интересно, посмотрите на это.

    SpringWebApp Dissection

    Теперь мы можем взглянуть на конфигурацию Spring Security. Файл web.xml для проекта настраивает его как приложение Spring и указывает файл applicationContext-security.xml в качестве исходного файла конфигурации Spring. Давайте внимательнее посмотрим на этот файл, так как именно здесь происходит большая часть магии:
    Давайте рассмотрим каждый из номеров строк, чтобы описать их функциональность. Строки с 3 по 5 инструктируют Spring искать классы, поддерживаемые Spring, в каталоге com.acme, и аннотации Spring будут поддерживаться. Строка 7 используется для загрузки свойств, указанных в файле application.properties (используется для указания хоста API). Строки с 9 по 11 включают Spring Security для приложения. Обычно, как дочерний элемент для http, вы должны указать, какие страницы должны быть защищены с помощью ролей, но для простоты этого примера это не было настроено.

    В строках 13-17 начинаются настройки для базового Spring Security. Мы определяем пользовательского провайдера аутентификации с именем userDetailsSrv через его bean ref. Этот bean-компонент реализуется через пользовательский класс com.acme.security.UserDetailsService (строка 19). Давайте внимательнее посмотрим на этот класс:

    Как видите, этот класс реализует интерфейс Spring org.springframework.security.core.userdetails.UserDetailsService. Это требует переопределения метода loadUserByUsername. Этот метод отвечает за извлечение пользователя из провайдера / источника аутентификации. Возвращенный пользователь (или, если не найдено ни одного подходящего пользователя, генерируется исключение UsernameNotFoundException — строка 28) должен содержать свойство пароля, чтобы Spring Security сравнил его с данными, указанными в форме. В этом случае, как мы видели ранее, пароль возвращается в хэше SHA-256.

    В нашей реализации API поиск пользователя выполняется с использованием класса APIHelper, о котором мы расскажем далее. Возвращенные данные API затем заполняются в пользовательском классе с именем UserDetails. Это реализует интерфейс Spring с тем же именем. Этот интерфейс требует конкретной реализации методов getUsername () и getPassword (). Spring вызовет их на следующем шаге обработки Security для сравнения этих значений с тем, что было записано в веб-форме.

    Как Spring сравнивает пароль, возвращаемый в SHA-256, со значением пароля формы. Если вы оглянетесь назад на конфигурацию XML, она содержала этот параметр:
    Обратите внимание на passwordEncoder — эта ссылка указывает на класс Spring ShaPasswordEncoder. Этот класс будет вычислять пароль SHA-256 для пароля, предоставленного через веб-форму, а затем Spring будет сравнивать это вычисленное значение с тем, что мы вернули через API.

    Давайте закроем это, посмотрев на класс APIHelper:

    Первое, что вы увидите в строках 8 и 9, — это внедрение свойства API.host. Как вы помните, это было установлено в файле application.properties. Он идентифицирует хост, на котором нужно отправить вызов API (поскольку он выполняется локально, указывается localhost). Строки с 17 по 20 используют один из клиентских механизмов RESTEasy для публикации вызова JSON RESTful (RESTEasy также имеет так называемую реализацию прокси-клиента, которая более проста в использовании / меньше кода, но не обеспечивает такого же низкоуровневого управления). Полученный ответ от API затем преобразуется из JSON в объект Java пользователя путем Джексона в строке 26. Затем этот объект Java возвращается в службу UserDetails.

    Резюме / Итоговое

    Как видите, реальная работа по настройке Spring Security для аутентификации на основе вызова API (или на самом деле любой внешней службы) действительно довольно проста. Должно быть реализовано всего несколько классов, но может быть сложно попытаться выяснить это впервые. Отсюда и причина, по которой я включил полный сквозной пример.

    Ссылка: Spring Security с использованием API-аутентификации от нашего партнера JCG Джеффа Дэвиса в блоге Jeff’s SOA Ruminations .