Як приклади можна навести X.509, Siteminder та автентифікацію контейнером Java EE, в якому працює програма. При використанні попередньої автентифікації Spring Security повинен:

  • Ідентифікувати користувача, який надає запит.

  • Отримати повноваження для користувача.

Деталі залежатимуть від зовнішнього механізму автентифікації. Користувач може бути ідентифікований за інформацією його сертифіката у випадку X.509, або за заголовком HTTP-запиту у випадку Siteminder. Якщо покладатися на автентифікацію контейнера, користувач буде ідентифікований викликом методу getUserPrincipal() за вхідним запитом HTTP. У деяких випадках зовнішній механізм може надати інформацію про роль/повноваження користувача, але в інших випадках повноваження необхідно отримувати з окремого джерела, такого як UserDetailsService.

Класи фреймворку попередньої аутентифікації

Оскільки більшість механізмів попередньої аутентифікації випливають з одного й того самого шаблону, Spring Security містить набір класів, які забезпечують внутрішній фреймворк для реалізації постачальників попередньої аутентифікації. Це дозволяє усунути дублювання та додавати нові реалізації у структурованому вигляді, без необхідності писати все з нуля. Про ці класи не потрібно нічого знати, якщо потрібно використовувати щось на кшталт аутентифікації на основі X.509, оскільки вона передбачає варіант конфігурації простору імен, який більш простий у використанні і з яким простіше починати працювати. Якщо необхідно використовувати явну конфігурацію бінів або ти плануєш написати власну реалізацію, то розуміння того, як працюють реалізації, що передаються, буде корисним. Знайти класи можна у org.springframework.security.web.authentication.preauth. Тут ми наведемо лише загальний опис, тому тобі слід звернутися до Javadoc і джерел, коли це буде потрібно.

AbstractPreAuthenticatedProcessingFilter

Цей клас перевіряє поточний вміст контексту безпеки і, якщо він пустий, намагається дістати інформацію про користувача з HTTP-запиту та передати її AuthenticationManager. Підкласи перевизначають такі методи для отримання цієї інформації:

Перевизначаємо AbstractPreAuthenticatedProcessingFilter
Java

protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
Kotlin

protected abstract fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Ані?
protected abstract fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any?

Після їх виклику фільтр створить PreAuthenticatedAuthenticationToken, який містить дані, що повертаються, і відправить його на автентифікацію. Під "аутентифікацією" тут ми насправді маємо на увазі лише подальшу обробку для можливого завантаження повноважень користувача, але при дотриманні стандартної архітектури аутентифікації Spring Security.

Як і інші фільтри аутентифікації Spring Security, фільтр попередньої аутентифікації має властивість authenticationDetailsSource, яке за замовчуванням створює об'єкт WebAuthenticationDetails для зберігання додаткової інформації, такої як ідентифікатор сесії та IP-адреса джерела, у властивості details об'єкта Authentication . У випадку, якщо інформацію про роль користувача можна отримати з механізму попередньої аутентифікації, ці дані також зберігатимуться в цій властивості з урахуванням відомостей, що реалізують інтерфейс GrantedAuthoritiesContainer. Це дозволяє постачальнику автентифікації зчитувати повноваження, виділені користувачу ззовні. Далі ми розглянемо конкретний приклад.

J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource

Якщо фільтр налаштований з урахуванням authenticationDetailsSource, який є екземпляром цього класу, інформація про повноваження отримує isUserInRole(String role) для кожної "ролі, що відображається" із заздалегідь визначеного набору. Клас отримує їх від налаштованого MappableAttributesRetriever. Можливі варіанти реалізації передбачають жорстке кодування списку в контексті програми та зчитування інформації про роль з інформації в розділі <security-role> у файлі web.xml. У прикладі програми попередньої аутентифікації використовується останній підхід.

Існує додатковий етап, на якому ролі (або атрибути) відображаються на об'єкти GrantedAuthority зі Spring Security через налаштований Attributes2GrantedAuthoritiesMapper. За замовчуванням до імен просто додається звичайний префікс ROLE_, але це дає повний контроль над логікою роботи. об'єкт UserDetails для користувача. Це робиться шляхом делегування повноважень сервісу AuthenticationUserDetailsService. Останній схожий на стандартний UserDetailsService, але приймає об'єкт Authentication, а не просто ім'я користувача:


public interface AuthenticationUserDetailsService {
    UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}

Цей інтерфейс може мати й інші застосування, але за попередньої автентифікації він дозволяє отримати доступ до повноважень, які були упаковані в об'єкт Authentication, що можна було спостерігати у попередньому розділі. Це здійснює клас PreAuthenticatedGrantedAuthoritiesUserDetailsService. В якості альтернативи він може делегувати повноваження стандартному UserDetailsService через реалізацію UserDetailsByNameServiceWrapper.

Http403ForbiddenEntryPoint

AuthenticationEntryPoint відповідає за запуск процесу автентифікації для неавтентифікованого користувача (якщо він намагається отримати доступ до захищеного ресурсу), але у разі попередньої автентифікації не застосовується. Ти можеш налаштувати ExceptionTranslationFilter із використанням екземпляра цього класу тільки в тому випадку, якщо не використовуєш попередню автентифікацію у поєднанні з іншими механізмами автентифікації. Його виклик відбудеться, якщо користувач буде відхилений AbstractPreAuthenticatedProcessingFilter, що призведе до null-автентифікації. Під час його виклику завжди повертається код відповіді 403-forbidden.

Конкретні реалізації

Аутентифікація на основі X.509 описана в окремому розділі. Тут ми розглянемо деякі класи, які забезпечують підтримку інших сценаріїв попередньої аутентифікації.

Аутентифікація за заголовком запиту (Siteminder)

Зовнішня система аутентифікації може передавати інформацію додатка, встановлюючи певні заголовки в HTTP-запиті. Наочним прикладом такої системи є Siteminder, який передає ім'я користувача в заголовку SM_USER. Цей механізм підтримується класом RequestHeaderAuthenticationFilter, який витягує ім'я користувача з заголовка. За замовчуванням як ім'я заголовка використовується ім'я SM_USER. Детальнішу інформацію див. у javadoc.

Зверни увагу, що при використанні подібної системи фреймворк взагалі не виконує ніяких автентифікацій, тому дуже важливо, щоб зовнішня система була належним чином налаштована і забезпечувала захист будь-якого звернення до додатка. Якщо зловмисник зможе підробити заголовки у вихідному запиті таким чином, що це не можна буде виявити, він зможе обрати будь-яке ім'я користувача на свій розсуд.

Приклад конфігурації з використанням Siteminder

Типова конфігурація з використанням цього фільтра виглядає так:


<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
	<bean id="userDetailsServiceWrapper"
          class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
	<property name="userDetailsService" ref="userDetailsService"/>
	</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>

Тут ми припустили, що простір імен безпеки використовується для конфігурації. Також передбачається, що UserDetailsService був доданий (за назвою "userDetailsService") до конфігурації, щоб завантажити ролі користувача.

Аутентифікація на рівні контейнера Java EE

Клас J2eePreAuthenticatedProcessingFilter буде вилучати ім'я користувача з властивості userPrincipal для HttpServletRequest. Використання цього фільтра зазвичай поєднується з використанням ролей Java EE, як описано вище в підрозділі, присвяченому J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.

У проєкті зі зразками є зразок програми, в якому використовується цей підхід, тому візьми код з GitHub і подивися на файл контексту програми, якщо тобі цікаво.