本文我们讨论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
ExceptionTranslationFilterFilterSecurityInterceptor- 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(); //拒绝访问
}
}