本文我们讨论Spring Security在基于Servlet的应用程序中的高级体系结构。我们将在参考资料的身份验证、授权和保护漏洞部分中构建对这种高级理解。
过滤器综述
Spring Security的Servlet支持是基于Servlet过滤器的,所以通常先看看过滤器的角色是有帮助的。下图显示了单个HTTP请求处理程序的典型分层。
客户端向应用程序发送一个请求,容器创建一个FilterChain
,其中包含Filter[过滤器]
和Servlet
,它们应该根据请求URI的路径处理HttpServletRequest
。在Spring MVC应用程序中,Servlet
是DispatcherServlet
的一个实例。最多一个Servlet
可以处理单个HttpServletRequest
和HttpServletResponse
。但是,可以使用多个Filter:
- 阻止调用下游
Filter[过滤器]
或Servlet
。在这个实例中,Filter[过滤器]
通常会写入HttpServletResponse
。 - 修改下游
过滤器
和Servlet
使用的HttpServletRequest
或HttpServletResponse
。
Filter
的强大功能来自传递给它的FilterChain
。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 在应用程序的其余部分之前执行某些操作
chain.doFilter(request, response); // 调用应用程序的其余部分
// 在应用程序的其余部分之后做些什么
}
因为一个Filter
只影响下游的Filters
和Servlet
,所以每个Filter
被调用的顺序是非常重要的。
DelegatingFilterProxy
Spring提供了一个名为DelegatingFilterProxy
的过滤器实现,它允许在Servlet容器的生命周期和Spring的ApplicationContext
之间建立桥梁。Servlet容器允许使用自己的标准注册过滤器,但它不知道Spring定义的Bean。DelegatingFilterProxy
可以通过标准的Servlet容器机制注册,但可以将所有工作委托给实现Filter的Spring Bean。
下面是DelegatingFilterProxy
如何适应Filter[过滤器]和FilterChain
的图片。
DelegatingFilterProxy
从ApplicationContext
中查找Bean Filter0
,然后调用Bean Filter0
。DelegatingFilterProxy
的伪代码如下所示。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// 惰性地获取注册为Spring Bean的Filter
// 例如DelegatingFilterProxy委托是一个Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// 将工作委托给Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilterProxy
的另一个好处是,它允许延迟查看Filter
Bean实例。这很重要,因为容器在启动之前需要注册Filter
实例。但是,Spring通常使用ContextLoaderListener
来加载Spring Bean,直到需要注册Filter
实例之后才会加载。
FilterChainProxy
Spring Security的Servlet支持包含在FilterChainProxy
中。FilterChainProxy
是Spring Security提供的一个特殊的Filter
,它允许通过SecurityFilterChain
委托给多个Filter实例。因为FilterChainProxy
是一个Bean,它通常被包装在DelegatingFilterProxy
中。
SecurityFilterChain
SecurityFilterChain
被FilterChainProxy
用来确定这个请求应该调用哪个Spring Security过滤器。
SecurityFilterChain
中的Security过滤器是典型的Bean,但它们是用FilterChainProxy
而不是DelegatingFilterProxy
注册的。FilterChainProxy
为直接注册Servlet容器或DelegatingFilterProxy
提供了许多优势。首先,它为Spring Security的所有Servlet支持提供了一个起点。由于这个原因,如果你试图排除Spring Security的Servlet支持的故障,在FilterChainProxy
中添加一个调试点是一个很好的开始。
第二,由于FilterChainProxy
是Spring Security使用的中心,它可以执行非可选的任务。例如,它清除SecurityContext
以避免内存泄漏。它还应用Spring Security的HttpFirewall
来保护应用程序免受某些类型的攻击。
此外,它在决定何时应该调用SecurityFilterChain
方面提供了更大的灵活性。在Servlet容器中,仅基于URL调用Filters。然而,FilterChainProxy
可以基于HttpServletRequest
中的任何东西来确定调用,通过利用RequestMatcher
接口。
事实上,FilterChainProxy
可以用来确定应该使用哪个SecurityFilterChain
。这允许为应用程序的不同部分提供完全独立的配置。
在多个SecurityFilterChain
图中,FilterChainProxy
决定应该使用哪个SecurityFilterChain
。只有第一个匹配的SecurityFilterChain
才会被调用。如果一个/api/messages/
的URL被请求,它会首先匹配SecurityFilterChain0
的/api/**
模式,所以只有SecurityFilterChain0
会被调用,即使它也匹配SecurityFilterChainn
。如果请求的URL是/messages/
,它将不会匹配SecurityFilterChain0
的/api/**
模式,因此FilterChainProxy
将继续尝试每个SecurityFilterChain
。假设没有其他实例,SecurityFilterChain
实例匹配SecurityFilterChainn
将被调用。
注意,SecurityFilterChain0
只配置了三个Security Filter实例。然而,SecurityFilterChainn
配置了四个Security Filter。需要注意的是,每个SecurityFilterChain
都可以是惟一的,并且可以单独配置。事实上,如果应用程序希望Spring Security忽略某些请求,SecurityFilterChain
可能没有安全过滤器。
Security 过滤器
Security Filters通过SecurityFilterChain
API插入到FilterChainProxy
中。过滤器的顺序很重要。通常不需要知道Spring Security的过滤器的顺序。然而,有时候知道顺序是有益的。
下面是Spring Security Filter排序的综合列表:
- 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
处理Security异常
ExceptionTranslationFilter
允许将AccessDeniedException
和AuthenticationException
转换为HTTP响应。
ExceptionTranslationFilter
作为一个Security过滤器被插入到FilterChainProxy
中。
-
首先,
ExceptionTranslationFilter
调用FilterChain.doFilter(request、response)
来调用应用程序的其余部分。 -
如果用户没有通过身份验证,或者是
AuthenticationException
,则启动身份验证。- 清除
SecurityContextHolder
HttpServletRequest
被保存在RequestCache
中。当用户成功通过身份验证时,使用RequestCache
重现原始请求。AuthenticationEntryPoint
用于从客户端请求凭据。例如,它可能重定向到登录页面或发送WWW-Authenticate
报头。
- 清除
-
否则,如果是
AccessDeniedException
,则AccessDenied
。调用AccessDeniedHandler
来处理被拒绝的访问。
Note : 如果应用程序没有抛出AccessDeniedException
或AuthenticationException
,则ExceptionTranslationFilter
不会做任何事情。
ExceptionTranslationFilter
的伪代码看起来像这样:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); //启动身份验证
} else {
accessDenied(); //拒绝访问
}
}