spring security核心探秘-基础原理

spring security核心探秘与运行原理解析

Posted by yishuifengxiao on 2020-11-15

一 基本原理

Spring Security的Servlet支持基于Servlet过滤器,因此通常首先了解过滤器的作用会很有帮助。 下图显示了单个HTTP请求的处理程序的典型分层。

filterchain

Spring提供了一个名为DelegatingFilterProxy的Filter实现,可以在Servlet容器的生命周期和Spring的ApplicationContext之间进行桥接。 Servlet容器允许使用其自己的标准注册Filters,但是它不知道Spring定义的Bean。 DelegatingFilterProxy可以通过标准的Servlet容器机制进行注册,但是可以将所有工作委托给实现Filter的Spring Bean。

这是DelegatingFilterProxy如何适合Filters和FilterChain的图片。

delegatingfilterproxy

DelegatingFilterProxy从ApplicationContext中查找Bean Filter0,然后调用Bean Filter0。下面可以看到DelegatingFilterProxy的伪代码。

1
2
3
4
5
6
7
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);
}

DelegatingFilterProxy的另一个好处是它允许延迟查找filterbean实例。这一点很重要,因为容器需要在容器启动之前注册过滤器实例。但是,Spring通常使用ContextLoaderListener来加载springbean,直到需要注册过滤器实例之后才会加载。

springsecurity的Servlet支持包含在FilterChainProxy中。FilterChainProxy是springsecurity提供的一种特殊的过滤器,它允许通过SecurityFilterChain委托给许多过滤器实例。因为FilterChainProxy是一个Bean,它通常被包装在DelegatingFilterProxy中。

delegatingfilterproxy

FilterChainProxy使用SecurityFilterChain来确定应该为此请求调用哪些Spring安全过滤器。

delegatingfilterproxy

SecurityFilterChain中的安全过滤器通常是bean,但是它们注册到FilterChainProxy而不是DelegatingFilterProxy。FilterChainProxy为直接注册Servlet容器或DelegatingFilterProxy提供了许多优点。首先,它为springsecurity的所有Servlet支持提供了一个起点。出于这个原因,如果您试图对springsecurity的Servlet支持进行故障排除,那么在FilterChainProxy中添加一个调试点是一个很好的起点。

其次,由于FilterChainProxy是Spring安全使用的核心,它可以执行不被视为可选的任务。例如,它清除SecurityContext以避免内存泄漏。它还应用springsecurity的HttpFirewall来保护应用程序免受某些类型的攻击。
此外,它在确定何时应该调用SecurityFilterChain时提供了更大的灵活性。在Servlet容器中,仅根据URL调用过滤器。但是,FilterChainProxy可以利用RequestMatcher接口根据HttpServletRequest中的任何内容来确定调用。

事实上,FilterChainProxy可以用来确定应该使用哪个SecurityFilterChain。这允许为应用程序的不同切片提供完全独立的配置。

二 过滤器的顺序

安全筛选器通过SecurityFilterChain API插入到FilterChainProxy中。 过滤器的顺序很重要。 通常不必知道Spring Security的过滤器的顺序。 但是,有时候了解顺序是有益的

以下是Spring Security过滤器顺序的完整列表:

  • 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

三 应用授权的工作原理

FilterSecurityInterceptor为HttpServletRequests提供授权。它作为安全过滤器之一插入FilterChainProxy。

delegatingfilterproxy

  1. FilterSecurityInterceptor从SecurityContextHolder获得身份验证。
  2. FilterSecurityInterceptor从传递到FilterSecurityInterceptor的HttpServletRequest、HttpServletResponse和FilterChain创建FilterInvocation。
  3. 将FilterInvocation传递给SecurityMetadataSource以获取ConfigAttributes。
  4. 将身份验证、FilterInvocation和ConfigAttributes传递给AccessDecisionManager。
  5. 如果授权被拒绝,则抛出AccessDeniedException。在本例中,ExceptionTranslationFilter处理AccessDeniedException。
  6. 如果访问被授予,FilterSecurityInterceptor将继续使用FilterChain,它允许应用程序正常处理。

默认情况下,Spring安全性的授权将要求对所有请求进行身份验证。显式配置如下:

1
2
3
4
5
6
7
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
);
}

我们可以通过按优先级添加更多规则来配置spring security,使其具有不同的规则。

1
2
3
4
5
6
7
8
9
10
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize
.mvcMatchers("/resources/**", "/signup", "/about").permitAll()
.mvcMatchers("/admin/**").hasRole("ADMIN")
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().denyAll()
);
}
  • 这里指定了多个授权规则。每一条规则都是按照它们被声明的顺序来考虑的。我们指定了多个任何用户都可以访问的URL模式。特别是,如果URL以“/resources/”开头、或等于“/signup”或等于“/about”开头,则任何用户都可以访问请求。
  • 任何以“/admin/”开头的URL将被限制为具有“role\u admin”角色的用户。您会注意到,由于我们调用的是hasRole方法,所以不需要指定“ROLE_”前缀。
  • 任何以“/db/”开头的URL都要求用户同时具有“ROLEADMIN”和“ROLE_DBA”。您会注意到,由于我们使用的是hasRole表达式,所以不需要指定“ROLE”前缀。
  • 任何尚未在上匹配的URL都将被拒绝访问。如果您不想意外忘记更新授权规则,这是一个很好的策略。