Spring Security的功能可以概括为两部分,即认证和授权。
认证就是一个用户使用令牌(最常见的是账号和密码)登陆,服务器验证这个用户的令牌是否有效。
授权则是认证之后,系统根据这个用户的信息判断他拥有哪些权限,能访问哪种资源,能对资源进何种操作,并向他授予相关的权限,对没有权限访问的用户进行阻止。
这里说的Spring Security是基于Servlet容器的。
Spring Security的Servlet支持是基于Servlet的 Filter
的。Filter可以对请求进行处理,但Filter一般会将处理好的请求交给 FilterChain
中的下一个Filter,即一个个Filter按照相应的顺序组合起来形成了一个Filter链,从而达到对请求进行过滤/提前处理的效果。
当请求到达时,Servlet容器会初始化一个 FilterChain
,并将请求传递到里面进行处理:
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 } 复制代码
经过一个个Filter处理后请求才到达Servlet处理并响应,由此可见,Filter在 FilterChain
中组织的顺序非常重要。
因为Servlet容器跟Spring的IoC容器是两种不同的容器,Servlet容器无法获知Spring容器中各种Bean(用户自定义Filter)的信息,也无法直接使用这些Bean(Filter),所以需要在两者间建立桥梁,使得我们定义的Bean(Filter)能够被Servlet容器使用。
Spring提供了一种叫 DelegatingFilterProxy
的Filter实现。
它像普通的Filter一样被插入到 FilterChain
中,它的功能是将请求委托给Spring容器里的Bean Filter来处理,从而建立起Servlet容器和Spring容器之间的桥梁。
例如图中 DelegatingFilterProxy
会在Spring容器中寻找 Bean Filter0
,并将 ServletRequest
和 ServletResponse
传递过去:
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
的另一个好处是它可以延迟加载Bean Filter实例,因为 FilterChain
会在Servlet容器启动前初始化,这样可以提升Servlet容器启动的性能。
上面提到 DelegatingFilterProxy
会将请求委托给Bean Filter,而其实这里的Bean Filter也不会直接处理请求,会将请求进一步转发,这个Bean Filter即是 FilterChainProxy
。
FilterChainProxy
是Spring Security提供的特殊Filter,它会将请求委托到另一种 FilterChain
中,即Spring的FilterChain: SecurityFilterChain
。
我们可以很自然地想到, SecurityFilterChain
中便是Spring的Bean Filter了,即 SecurityFilter
:
这里的 SecurityFilter
就是普通的Bean,他们可以识别自己能处理的请求并对请求处理,否则将请求传给链中的下一个Filter,例如最常见的 UsernamePasswordAuthenticationFilter
可以识别处理基于表单的登陆请求。
值得注意的是, SecurityFilterChain
可以有多条,可以根据不同的请求来判断请求应该被传递到哪个 SecurityFilterChain
中:
比如符合 /api/**
匹配模式的URL应该被传递到最上面的 SecurityFilterChain
中,这个请求一旦匹配到了某个上游的 SecurityFilterChain
,下游的 SecurityFilterChain
就不会再进行匹配。
官方文档给出了按照Spring Security Filter排列顺序的列表:
Security Filters
ExceptionTranslationFilter
允许将 AccessDeniedException
和 AuthenticationException
异常转换为HTTP响应,从它的名字可以看到,它也是一个Filter,可以被插入到 SecurityFilterChain
中,这是它的工作流程:
AuthenticationException
,则会被它捕获(见下面代码),并调用它的 startAuthentication()
方法跳转进行认证。(图中的 SecurityContextHolder
等几个东西属于认证功能部分,可以先不用管) AccessDeniedException
则对异常作出响应,通知用户禁止访问。