web-security第二期:Spring Security 框架概览

阅读本文之前,我默认您已经掌握 spring-web spring-boot 的基础知识

本文的编写依据是  Spring Security 官方文档

Spring Security 是一个安全框架,前身是 Acegi Security,能够为 Spring 企业应用系统提供声明式的安全访问控制。Spring Security 基于 Servlet 过滤器、IoC 和 AOP,为 Web 请求和方法调用提供身份确认和授权处理,避免了代码耦合,减少了大量重复代码工作。

既然Spring Security是基于Filter来实现的,那咱们先从Filter 开始:

1.FilterChain

Filter是在Servlet容器启动前被注册,如下:这是一个典型的过滤链,通过(chain.doFilter(request, response))一层层的过滤请求

filterchain

2.DelegatingFilterProxy

Servlet容器允许使用其自己的标准注册Filters,但它不了解Spring定义的Bean,而DelegatingFilterProxy 在Servlet容器和Spring的ApplicationContext之间搭建了一个桥梁。使得Servlet可以注册在SpringApplicationContext中定义的Filter,如下图所示

delegatingfilterproxy

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中。

filterchainproxy

4. SecurityFilterChain

FilterChainProxy使用SecurityFilterChain确定应对此请求调用哪些Spring Security过滤器

securityfilterchain

SecurityFilterChain中的Security FIlter 通常是Bean,但它们使用的是FilterChainProxy而不是DelegatingFilterProxy注册的。 FilterChainProxy具有直接向Servlet容器或DelegatingFilterProxy注册的许多优点。例如,它为Spring Security的所有Servlet支持提供了一个起点。因此,如果您想对Spring Security的Servlet支持进行BUG排除,那么在FilterChainProxy中添加调试点是一个很好的选择。

实际上,FilterChainProxy 甚至可以确定来使用哪一个 SecurityFilterChain, 前提是你可以为你的应用程序提供多个完全独立的配置空间,如下图所示:

multi 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 (认证异常)

exceptiontranslationfilter

  • number 1 正常执行过滤链,如果发生异常,则判断是什么类型的异常

  •  number 2 如果是AuthenticationException,则对用户进行认证授权,步骤如下

    • 清除 SecurityContextHolder

    • 将请求放入 RequestCache 中进行缓存,如果用户认证通过,则从这里重发此请求

    • AuthenticationEntryPoint用于从客户端请求凭据

  • number 3 如果它是AccessDeniedException,则拒绝访问。并调用AccessDeniedHandler处理拒绝的访问

注:如果应用程序未引发AccessDeniedException或AuthenticationException,则ExceptionTranslationFilter不执行任何操作。

伪代码如下:

try {
    filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException e) {
    if (!authenticated || e instanceof AuthenticationException) {
        startAuthentication(); 
    } else {
        accessDenied(); 
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42013035/article/details/106627031