В этом разделе обсуждается высокоуровневая архитектура 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
.

DelegatingFilterProxy
осуществляет поиск 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
следует использовать. Это позволяет обеспечить совершенно отдельную конфигурацию для различных фрагментов вашего приложения.

На рисунке c несколькими SecurityFilterChain экземпляр FilterChainProxy
решает, какую 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 в качество одного из фильтров безопасности.

-
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
, то запустите процедуру аутентификации. - В противном случае в доступе будет отказано
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ