Spring Security 是一个安全框架,前身是 Acegi Security,能够为 Spring 企业应用系统提供声明式的安全访问控制。Spring Security 基于 Servlet 过滤器、IoC 和 AOP,为 Web 请求和方法调用提供身份确认和授权处理,避免了代码耦合,减少了大量重复代码工作。
既然Spring Security是基于Filter来实现的,那咱们先从Filter 开始:
1.FilterChain
Filter是在Servlet容器启动前被注册,如下:这是一个典型的过滤链,通过(chain.doFilter(request, response))一层层的过滤请求
2.DelegatingFilterProxy
Servlet容器允许使用其自己的标准注册Filters,但它不了解Spring定义的Bean,而DelegatingFilterProxy 在Servlet容器和Spring的ApplicationContext之间搭建了一个桥梁。使得Servlet可以注册在SpringApplicationContext中定义的Filter,如下图所示
DelegatingFilterProxy从ApplicationContext查找Bean Filter0,然后调用Bean Filter0。 伪代码如下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilter还有一个好处:延迟注册Filter Bean
在 spring-web第一期 我曾分析过,因为Servlet容器需要在容器启动之前注册Filter实例。但是,Spring通常使用ContextLoaderListener加载Spring Bean,因此在容器启动前是不可以获取Spring Bean的,直到Spring容器启动后才可以注册这些Filter。
3. FilterChainProxy
Spring Security的对Servlet的支持包含在FilterChainProxy中。 FilterChainProxy是Spring Security提供的特殊过滤器,允许通过SecurityFilterChain委派许多过滤器实例。由于FilterChainProxy是一个Bean,因此通常将其包装在DelegatingFilterProxy中。
4. SecurityFilterChain
FilterChainProxy使用SecurityFilterChain确定应对此请求调用哪些Spring Security过滤器
SecurityFilterChain中的Security FIlter 通常是Bean,但它们使用的是FilterChainProxy而不是DelegatingFilterProxy注册的。 FilterChainProxy具有直接向Servlet容器或DelegatingFilterProxy注册的许多优点。例如,它为Spring Security的所有Servlet支持提供了一个起点。因此,如果您想对Spring Security的Servlet支持进行BUG排除,那么在FilterChainProxy中添加调试点是一个很好的选择。
实际上,FilterChainProxy 甚至可以确定来使用哪一个 SecurityFilterChain, 前提是你可以为你的应用程序提供多个完全独立的配置空间,如下图所示:
FilterChainProxy决定应使用哪个SecurityFilterChain(每个FilterChain中拥有的Filter个数可不相同)。仅匹配的第一个SecurityFilterChain将被调用。如果请求的URL是/ api / messages /,则它将首先与SecurityFilterChain0的/ api / **模式匹配,因此,即使SecurityFilterChain0也与SecurityFilterChainn匹配,也只会调用它。假设都没有匹配上,那么默认使用 SecurityFilterChain n (即最后一个)
5.SecurityFilters的顺序
SecurityFilters 通过SecurityFilterChain API插入到FilterChainProxy中。过滤器的顺序很重要。虽然通常不必要知道Spring Security的过滤器的顺序。但是,有时候了解还是有好处的,如下是Security Filters的完整列表:(蓝色部分是较为重要的过滤器)
- ChannelProcessingFilter
- ConcurrentSessionFilter
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- CsrfFilter
- LogoutFilter
- OAuth2AuthorizationRequestRedirectFilter
- Saml2WebSsoAuthenticationRequestFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- Saml2WebSsoAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- ConcurrentSessionFilter
- OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- OAuth2AuthorizationCodeGrantFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- SwitchUserFilter
6. Handling Security Exceptions
既然过滤链这么多,那么如果在哪一个环节碰到异常该怎么抛出了,最理想的办法当然是将异常以可阅读,可定位的方式反馈给客户端(如使用HTTP返回)
我们先来说说异常,Spring Security 中最常见的两个异常是:
- AccessDeniedException (拒绝访问异常)
- AuthenticationException (认证异常)
- 正常执行过滤链,如果发生异常,则判断是什么类型的异常
- 如果是AuthenticationException,则对用户进行认证授权,步骤如下
- 清除 SecurityContextHolder
- 将请求放入 RequestCache 中进行缓存,如果用户认证通过,则从这里重发此请求
- AuthenticationEntryPoint用于从客户端请求凭据
- 如果它是AccessDeniedException,则拒绝访问。并调用AccessDeniedHandler处理拒绝的访问
注:如果应用程序未引发AccessDeniedException或AuthenticationException,则ExceptionTranslationFilter不执行任何操作。
伪代码如下:
try {
filterChain.doFilter(request, response);
} catch (AccessDeniedException | AuthenticationException e) {
if (!authenticated || e instanceof AuthenticationException) {
startAuthentication();
} else {
accessDenied();
}
}