Spring Security过滤链参考文档

一、简介

Spring Security是一个提供身份验证,授权和保护以防止常见攻击的框架,需要Java 8或更高版本的运行环境。

它通过使用标准的Servlet Filter来集成Servlet容器,这意味着它可以与在Servlet容器中运行的任何应用程序一起工作。

更具体地说,您无需在基于Servlet的应用程序中使用Spring即可利用Spring Security。

二、Spring Security 过滤链

Spring Security对Servlet的支持是基于Servlet的Filter,因此首先了解Filter是非常有帮助的。

下图显示了单个 HTTP 请求的处理程序的典型分层:

客户端向应用程序发送请求,然后容器创建了一个FilterChain,这个FilterChain含许多的Filter和一个Servlet,

这些Filter和Servlet能够对基于请求URI路径的HttpServletRequest进行处理。

在Spring MVC应用中,Servlet是一个DispatcherServlet实例。

最多只能有一个Servlet处理HttpServletRequest和HttpServletResponse,

但是Filter都可以用来阻止下游的Filter或Servlet被调用,或修改下游Filter的HttpServletRequest或HttpServletResponse。

例如:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}

Spring提供了一个Filter接口的实现类(DelegatingFilterProxy),它连接了Servlet容器的生命周期和Spring的ApplicationContext。

Servlet容器允许用它自己的标准注册过滤链,但是它不知道Spring定义的Bean。

DelegatingFilterProxy可以通过标准Servlet容器机制进行注册,但是它将所有工作委托给实现了Filter接口的Bean实例。

下图是对上文的介绍:

DelegatingFilterProxy从ApplicationContext中查找Filter的第一个Bean,然后调用该Bean。

另外,DelegatingFilterProxy允许延迟加载Filter Bean实例,因为容器需要在启动前注册Filter实例。

然而,Spring通常等到Filter注册完后才用ContextLoaderListener去加载Filter实例。

Spring Security对Servlet的支持主要靠FilterChainProxy,它是Spring Security提供的一个特别的Filter。

FilterChainProxy允许委派许多Filter实例通过SecurityFilterChain(它是一个Bean,通常被包裹在DelegatingFilterProxy中)。

如图所示:

 FilterChainProxy使用SecurityFilterChain决定为请求调用哪个SpringSecurity Filter。

在SecurityFilterChain中的Security Filter通常都是Bean,它们通常都是被FilterChainProxy注册,而不是DelegatingFilterProxy。

FilterChainProxy为直接注册在DelegatingFilterProxy或Servlet提供了很多优势:

1、FilterChainProxy为Spring Security 的所有Servlet支持提供了一个入口。

2、FilterChainProxy为决定什么时候应该调用SecurityFilterChain提供了灵活性。

因此,如果您尝试对 Spring Security的Servlet支持进行故障排除,则为FilterChainProxy打断点调试是一个很好的开始。

在Servlet容器中,Filter的调用仅仅基于URL,但FilterChainProxy通过调用RequestMatcher接口可以决定任何调用。

事实上,FilterChainProxy可以用于决定哪个SecurityFilterChain应该被调用:

若有多个SecurityFilterChain,FilterChainProxy将决定那个SecurityFilterChain应该被使用。

只有第一个匹配的SecurityFilterChain将会被调用。

如果一个请求URL是"/api/messages"的话,那么只有SecurityFilterChain0将会被调用,甚至该URL也匹配其他的。

三、处理安全异常

ExceptionTranslationFilter允许将AccessDeniedException和AuthenticationException反应给HTTP响应。

ExceptionTranslationFilter作为安全Filter之一被插入到FilterChainProxy。

首先ExceptionTranslationFilter调用FilterChain.doFilter(request,response)去调用剩余的应用程序。

如果用户没有认证或者发生认证异常,然后就开始认证。

SecurityContextHolder被清空,HttpServletRequest保存在RequestCache中,

当认证成功后,RequestCache将重放原始请求。

AuthenticationEntryPoint用来向客户端请求凭据,他可能是重定向到登陆页面或者发送一个WWW-Authenticate头部。

否则,如果是AccessDeniedException,那么AccessDenieHandler将会被调用。

用伪代码表示他将会是这样:

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

四、认证

1、SecurityContextHolder:Spring Security存储认证信息的地方。

2、SecurityContext:从SecurityContextHolder中获取,它含有当前已通过身份认证的用户。

3、Authentication:SecurityContext中的当前用户,或者是用户提供给AuthenticationManager验证的凭证。

4、GrantedAuthority:拥有的权限。

5、AuthenticationManager:定义Spring Security的Filter如何执行身份验证的API。

6、ProviderManager:AuthenticationManager最常见的实现类。

7、AuthenticationProvider:ProviderManager用来执行特定的身份验证。

8、AbstractAuthenticationProcessingFilter:身份验证的基础。

Ⅰ、SecurityContextHolder是身份验证的核心:

用户经过身份验证最简单的方法是直接设置SecurityContextHolder(存储认证信息的地方)。

SecurityContext context = SecurityContextHolder.createEmptyContext(); 
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER"); 
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); 

1、创建一个新的SecurityContext实例而不是用SecurityContextHolder.getContext().setAuthentication(authentication)

是很重要的,它可以避免多线程下的竟态条件。

2、Spring Security不关心在SecurityContext上关于Authentication什么类型的实现,这里使用的是TestingAuthenticationToken,

是因为它很简单,通常使用的都是UsernamePasswordAuthenticationToken(userDetails,password,authorities)

3、最后,在SecurityContextHolder上设置SecurityContext,Spring Security将会使用认证信息。

如果你想要获取验证信息,你可以像下面这样做:

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

默认情况下,SecurityContextHolder使用ThreadLocal去存储这些信息。

这意味着对于相同线程下的方法,甚至不将它作为参数传递给其他方法,SecurityContext也是可用的。

如果处理当前主体的请求后想清除线程,这种情况下也是非常安全的。

因为Spring Security的FilterChainProxy确保SecurityContext总是被清除的。

Ⅱ、Authentication在Spring Security中有两个主要用途:

1、作为AuthenticationManager中的输入提供身份验证的凭据。

2、代表当前认证通过的用户,可以从SecurityContext中获取。

Authentication包含如下:

principal:标识用户。使用用户名/密码进行身份验证时,这通常是UserDetails实例。

credentials:通常是密码。在许多情况下,这将在用户经过身份验证后被清除,以确保它不被泄露。

authorities:授予用户的高级别权限。

Ⅲ、ProviderManager委托了一个AuthenticationProvider列表,每个AuthenticationProvider都有机会认证。

AuthenticationProvider可以提供一个特定的身份认证,例如:

DaoAuthenticationProvider通过用户名密码身份验证,JwtAuthenticationProvider通过JWT token身份认证。

ProviderManager还允许配置一个可选的父级AuthenticationManager,

如果没有一个AuthenticationProvider能够进行认证,就向它进行咨询,它通常是ProviderManager的实例。

实际上,多个ProviderManager实例可能共享相同的父级AuthenticationManager。

这是很常见的情况:多个SecurityFilterChain实例有相同的身份认证(相同的父级AuthenticaManager),

但是不同的验证机制(不用的ProviderManaer实例)。

默认情况下,ProviderManager将会试图清除认证成功的Authentication中的敏感信息,

这样可以防止密码之类的信息被保留的时间超过HttpSession所需的时间。

 Ⅳ、AuthenticationEntryPoint用于发送从客户端请求凭据的 HTTP 响应。

有时客户端会通过用户名密码去请求资源,在这种情况下,Spring Security不需要提供从客户端请求凭据的HTTP 响应。

其他情况下,客户端向它们无权访问的资源发出未经身份验证的请求,它的实现类通常用于请求用户凭据。

例如:重定向到登录页,用www-Authenticate头部进行响应。

 Ⅴ、AbstractAuthenticationProcessingFilter是最基础的用户身份凭据认证Filter。

在用户凭据被认证之前,Spring Security通常用AuthenticationEntryPoint请求用户凭据。

接下来,AbstractAuthenticationProcessingFilter能够验证任何提交给它的身份验证请求。

1、当用户提交凭据时,AbstractAuthenticationProcessingFilter从HttpServletRequest创建Authentication去验证,

被创建的Authentication依赖于AbstractAuthenticationProcessingFilter的子类。例如:UsernamePasswordAuthenticationFilter

通过提交给HttpServletRequest的用户名和密码来创建一个UsernamePasswordAuthenticationToken。

2、接下来这个Authentication被传递给AuthenticationManager去验证,如果验证失败:

那么SecurityContextHolder会被清空,RememberMeServices.loginFail会被调用,AuthenticationFailureHandler会被调用。

如果验证成功:

SessionAuthenticationStrategy将会收到新的登录通知,这个Authentication将会被设置到SecurityContextHolder中,

然后SecurityContextPersistenceFilter保存SecurityContext到HttpSession中。

RememberMeServices.loginSuccess被调用,如果没有配置记住我,它将会是no-op。

ApplicationEventPublisher发布InteractiveAuthenticationSuccessEvent,最后AuthenticationSuccessHandler被调用。

 Ⅵ、Username/Password Authentication

Spring Security提供了以下内置机制,用于从HttpServletRequest中读取用户名和密码。

Form Login、Basic Authentication、Digest Authentication。

以Form Login为例:

用户向未经授权的资源发出未经身份验证的请求"/private",

Spring Security的FilterSecurityInterceptor抛出AccessDeniedException异常提示该请求没有经过认证。

由于用户没有通过身份验证,ExceptionTranslationFilter通过AuthenticationEntryPoint发起身份认证并发送一个重定向到登录页。

大多数情况下,AuthenticationEntryPoint是LoginUrlAuthenticationEntryPoint的实例。

然后,浏览器将请求将其重定向到的登录页面,当用户名和密码被提交后:

UsernamePasswordAuthenticationFilter就开始验证用户名和密码,

UsernamePasswordAuthenticationFilter继承于AbstractAuthenticationProcessingFilter,所以下图看起来会很相似:

当用户提交用户名密码后,UsernamePasswordAuthenticationFilter创建一个UsernamePasswordAuthenticationToken,

它是从一个从HttpServletRequest抽取用户名和密码创建的一个Authentication类型。

接下来UsernamePasswordAuthenticationToken将会被传递给AuthenticationManager去认证。

 五、SpringSecurity Web 与 SpringSecurity OAuth2的区别与联系

想要知道他们之间的区别和联系,我们可以先了解下什么是OAuth2:OAuth2

OAuth2通常都是用于第三方登录的,那他们之间的区别通俗点说就是主和宾的关系。

posted @ 2021-01-15 19:00  M-Anonymous  阅读(407)  评论(0编辑  收藏  举报