Spring Security и токен-аутентификация: Простой вариант

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

В первом варианте будет использоваться стандартный UserDetails, следовательно, в передаваемом заголовке будет указывать имя пользователя.

Нам понадобится Spring Boot с Spring WebMVC, Spring Security и Spring Test:

Для начала напишем интеграционный тест, который поможет нам в разработке проекта. TDD — очень хорошая практика.

Тут всё достаточно просто:

  • Метод testGreeting тестирует оптимистичный сценарий работы нашего сервиса, когда пользователь аутентифицирован и получает доступ к сервису
  • Метод testGreetingUnauthorizedWithoutHeader тестирует вариант сценария, когда клиент не передаёт токен аутентификации вообще
  • Метод testGreetingUnauthorizedWithWrongHeaderValue тестирует вариант сценария, когда клиент передаёт токен, по которому не получится аутентифицировать пользователя

Теперь займёмся конфигурированием Spring Security:

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

    В principalRequestHeader мы указываем заголовок, значение которого будет использоваться в качестве токена аутентификации, а так же указываем фильтру, с каким менеджером аутентификации ему нужно работать. Так же я указал requestHeaderAuthenticationFilter.setExceptionIfHeaderMissing(false);, чтобы в случае отсутствия заголовка не было выкинуто исключение. Это может быть полезным, если используется больше одного способа аутентификации.
  2. Теперь нужно описать провайдера аутентификации, который будет пытаться аутентифицировать пользователя по переданному токену:

    Провайдеру нужно указать сервис, который будет предоставлять информацию о пользователе. В данном случае мы просто заворачиваем стандартный UserDetailsService в UserDetailsByNameServiceWrapper.
  3. Ну и наконец конфигурируем HttpSecutity и AuthenticationManager:

    Тут в целом всё понятно — мы добавляем наш фильтр в цепочку аутентификации и предоставляем доступ к /protected/** только пользователям прошедшим аутентификацию.

Теперь создадим простенький сервис, на котором протестируем работу токен-аутентификации:

На этом моменте интеграционные тесты должны пройти успешно, что говорит об успешности работы механизма аутентификации по токену.

В следующем посте я опишу более сложный вариант аутентификации пользователя по токену.