У цьому розділі обговорюється високорівнева архітектура Spring Security у додатках на базі сервлетів.
Короткий опис фільтрів
Підтримка сервлетів у Spring Security заснована на екземплярах Filter
сервлетів, тому спочатку корисно розглянути роль Filter
в цілому. На малюнку нижче показана типова
рівнева структура обробників для одного HTTP-запиту.
Клієнт відправляє запит
додатку, а контейнер створює FilterChain
який містить екземпляри Filter
та
Servlet
,
який повинен обробити HttpServletRequest
на основі шляху URI запиту. У програмі Spring MVC Servlet
є екземпляром DispatcherServlet
. Тільки один Servlet
може обробляти один HttpServletRequest
та HttpServletResponse
. Однак при цьому можна використовувати більше одного Filter
для:
Запобігання виклику
Filter
абоServlet
, які стоять нижче. У цьому випадкуFilter
зазвичай записуєHttpServletResponse
.Зміни
HttpServletRequest
абоHttpServletResponse
, які використовуються наступнимиFilter
таServlet
Потужність Filter
залежить від FilterChain
, який передається до нього.
FilterChain
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// робимо щось перед виконанням решти програми
chain.doFilter(request, response) ;
// invoke the rest of the application // робимо щось після виконання решти програми }
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// робимо щось перед виконанням решти програми
chain.doFilter(request, response)
// invoke the rest of the application // робимо що-небудь після виконання решти програми }
Оскільки Filter
впливає лише на Filter
та Servlet
, які стоять нижче,
порядок
виклику кожного Filter
надзвичайно важливий.
DelegatingFilterProxy
Spring передбачає
реалізацію Filter
під назвою DelegatingFilterProxy
, яка дозволяє встановити міст між життєвим циклом
контейнера сервлетів та ApplicationContext
зі Spring. Контейнер сервлетів дозволяє реєструвати
екземпляри Filter
, використовуючи власні стандарти, але йому невідомо про біни, визначені Spring.
DelegatingFilterProxy
можна зареєструвати через стандартні механізми контейнера сервлетів, але
водночас делегувати всю роботу біну Spring, що реалізує Filter
.
Тут показано, як DelegatingFilterProxy
вписується в схему з екземплярами Filter
та FilterChain
.
Delegate
здійснює пошук Bean Filter0 у ApplicationContext
і потім викликає Bean
Filter0. Псевдокод DelegatingFilterProxy
показаний нижче.
DelegatingFilterProxy
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// У відтермінованому режимі отримуємо фільтр, який був зареєстрований як бін Spring
// Для прикладу в DelegatingFilterProxy делегат є екземпляром Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// делегуємо роботу біну Spring delegate.doFilter(request, response);
}
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
// У відкладеному режимі отримуємо фільтр, який був зареєстрований як бін Spring
// Для прикладу в DelegatingFilterProxy делегат є екземпляром Bean Filter0
val delegate: Filter = getFilterBean(someBeanName)
// делегуємо роботу біну Spring delegate.doFilter(request, response)
}
Ще однією перевагою DelegatingFilterProxy
є те, що він дозволяє відкласти пошук
екземплярів бінів Filter
. Це важливо, оскільки контейнеру необхідно зареєструвати екземпляри Filter
перед запуском контейнера. Однак Spring зазвичай використовує ContextLoaderListener
для завантаження
бінів Spring, що відбувається тільки після реєстрації екземплярів Filter
.
FilterChainProxy
Засоби підтримки сервлетів Spring Security містяться в FilterChainProxy
. FilterChainProxy
—
це спеціалізований Filter
, що передбачається Spring Security, який дозволяє делегувати повноваження
багатьом екземплярам Filter
через <SecurityFilterChain
. Оскільки
FilterChainProxy
є біном, він зазвичай обернутий у DelegatingFilterProxy.
SecurityFilterChain
SecurityFilterChain
використовується FilterChainProxy
для визначення того, які Filter
із Spring Security необхідно викликати для цього запиту.
Фільтри безпеки в SecurityFilterChain
зазвичай є бінами, але вони зареєстровані в
FilterChainProxy
замість DelegatingFilterProxy
. FilterChainProxy
передбачає
низку переваг у порівнянні з реєстрацією безпосередньо в контейнері сервлетів або DelegatingFilterProxy. По-перше,
він забезпечує відправну точку для всіх засобів підтримки сервлетів у Spring Security. З цієї причини, якщо ти
намагатимешся усунути неполадки в засобах підтримки сервлетів Spring Security, почати найкраще буде з додавання
точки налагодження в FilterChainProxy
.
По-друге, оскільки FilterChainProxy
займає
центральне місце при використанні Spring Security, він може виконувати завдання, які не розглядаються як
опціональні. Наприклад, він очищає SecurityContext
, щоб уникнути витоку пам'яті. Він також застосовує
HttpFirewall
зі Spring Security для захисту додатків від певних типів атак.
Крім того, він
забезпечує більшу гнучкість при визначенні моменту, коли необхідно викликати SecurityFilterChain
. У
контейнері сервлетів екземпляри Filter
викликаються лише на основі URL-адреси. Однак FilterChainProxy
може визначити виклик на основі чого завгодно HttpServletRequest
, використовуючи інтерфейс RequestMatcher
.
Фактично, FilterChainProxy
можна використовувати для визначення того, який SecurityFilterChain
слід використовувати. Це дозволяє забезпечити абсолютно окрему конфігурацію для різних фрагментів твого додатку.
/cdn.javarush.com/images/article/5d90186d-8562-4c03-96bf-8300ce42fcf4/512.jpeg" alt="">
На малюнку з кількома
SecurityFilterChain екземпляр FilterChain
вирішує, яку SecurityFilterChain
слід
використовувати. Буде викликана
лише перша SecurityFilterChain
, що збігаєься. Якщо робиться запит на URL-адресу
/api/messages/
,
вона спочатку буде зіставлена з шаблоном SecurityFilterChain0
через /api/**
,
тому буде викликано лише SecurityFilterChain0
, навіть якщо він також буде відповідати
SecurityFilterChainn
. Якщо буде запит на URL-адресу /messages/
, вона не
буде зіставлена з шаблоном SecurityFilterChain0
через /api/**
, тому FilterChainProxy
продовжуватиме перебирати кожну SecurityFilterChain
. Передбачається, що інші екземпляри SecurityFilterChain
,
що збігаються з SecurityFilterChainn
, не будуть викликані.
Зверни увагу, що в SecurityFilterChain0
налаштовано лише три екземпляри Filter
. Однак у SecurityFilterChainn
налаштовані чотири Filter
. Важливо, що кожна SecurityFilterChain
може бути унікальною та
конфігурованою ізольовано. Насправді SecurityFilterChain
може мати нуль екземплярів Filter
,
якщо програмі потрібно, щоб Spring Security ігнорував певні запити.
Фільтри безпеки
Фільтри Spring
Security додаються до FilterChainProxy через API для SecurityFilterChain. Порядок екземплярів Filter
має значення. Зазвичай необхідності знати порядок проходження Filter
у Spring Security немає. Тим не
менш, у деяких випадках корисно знати, в якому порядку вони розташовані.
Нижче наведено повний упорядкований список фільтрів Spring Security:
ForceEagerSessionCreationFilter
ChannelProcessingFilter
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
CsrfFilter
LogoutFilter
OAuth2AuthorizationRequestRedirectFilter
Saml2WebSsoAuthenticationRequestFilter
X509AuthenticationFilter
AbstractPreAuthenticatedProcessingFilter
CasAuthenticationFilter
OAuth2LoginAuthenticationFilter
Saml2WebSsoAuthenticationFilter
UsernamePasswordAuthenticationFilter
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
ConcurrentSessionFilter
DigestAuthenticationFilter
BearerTokenAuthenticationFilter
BasicAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
JaasApiIntegrationFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
OAuth2AuthorizationCodeGrantFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
SwitchUserFilter
Обробка винятків безпеки
ExceptionTranslationFilter
дозволяє перетворювати AccessDeniedException
та AuthenticationException
на HTTP-відповіді.
ExceptionTranslationFilter
додається в FilterChainProxy в якість одного з фільтрів безпеки.
/article/b93ab541-1f33-41c5-87db-87b46cfff1a6/512.jpeg" alt="">
1. Спочатку
ExceptionTranslationFilter
звертається доFilterChain.doFilter(request, response)
, щоб викликати решту програми.2. Якщо користувач не автентифікований або виникає
AuthenticationException
, запусти процедуру аутентифікації.TheSecurityContextHolder очищається
HttpServletRequest
зберігається вRequestCache
. Після того, як користувач успішно пройде аутентифікацію,RequestCache
буде використаний для відтворення вихідного запиту.AuthenticationEntryPoint
використовується для облікових запитів даних у клієнта. Наприклад, він може перенаправити на сторінку входу в систему або відправити заголовокWWW-Authenticate
.
3. Інакше, якщо виникне
AccessDeniedException
, то в доступі буде відмовлено.AccessDeniedHandle
r
викликається для обробки відмови у доступі.
Якщо програма не генерує AccessDeniedException
або AuthenticationException
,
то ExceptionTranslationFilter
нічого не робить.
Псевдокод для ExceptionTranslationFilter
виглядає приблизно так:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication();
} else {
accessDenied();
}
}
- З короткого опису екземплярів
Filter
можна згадати, що викликFilterChain.doFilter(request, response)
еквівалентний виклику решти програми. Це означає, що якщо інша частина програми (наприклад,FilterSecurityInterceptor
або метод безпеки) згенеруєAuthenticationException
абоAccessDeniedException
, то ці винятки будуть перехоплені та оброблені на цьому етапі. - Якщо користувач не автентифікований або з'являється
AuthenticationException
, то запусти процедуру автентифікації. - Інакше доступ буде відмовлено
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ