上一节中,主要讲了Spring Security认证和授权的核心组件及核心方法。但是,什么时候调用这些方法呢?答案就是Filter和AOP。Spring Security在我们进行用户认证以及授予权限的时候,通过各种各样的拦截器来控制权限的访问。
对于基于HttpRequest的方式对端点进行保护,我们使用一个Filter Chain来保护;对于基于方法调用进行保护,我们使用AOP来保护。本篇重点讲Spring Security中过滤器链的种类及过滤器中如何实现的认证和授权。
Spring Security会默认为我们添加15个过滤器,我们可以从WebSecurity(WebSecurity是Spring Security加载的一个重要对象,将在下节具体讲述)的performBuild()方法中看到过滤器链SecurityFilterChain的构建过程,并交由FilterChainProxy对象代理。我们从SecurityFilterChain的默认实现类DefaultSecurityFilterChain中的log看出,Spring Security由以下过滤器组成了过滤器链:
Creating filter chain: any request, [ org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7f353a0f, org.springframework.security.web.context.SecurityContextPersistenceFilter@4735d6e5, org.springframework.security.web.header.HeaderWriterFilter@314a31b0, org.springframework.security.web.csrf.CsrfFilter@4ef2ab73, org.springframework.security.web.authentication.logout.LogoutFilter@57efc6fd, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@d88f893, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@2cd388f5, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@7ea2412c, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2091833, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@4dad0eed, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@16132f21, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1c93b51e, org.springframework.security.web.session.SessionManagementFilter@59edb4f5, org.springframework.security.web.access.ExceptionTranslationFilter@104dc1a2, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@1de0641b ]
下面就是各个过滤器的功能,其中SecurityContextPersistenceFilter,UsernamePasswordAuthenticationFilter及FilterSecurityInterceptor分别对应了上节SecurityContext,AuthenticationManager,AccessDecisionManager的处理。
[WebAsyncManagerIntegrationFilter] (异步方式)提供了对securityContext和WebAsyncManager的集成。方式是通过SecurityContextCallableProcessingInterceptor的beforeConcurrentHandling(NativeWebRequest, Callable)方法来将SecurityContext设置到Callable上。其实就是把SecurityContext设置到异步线程中,使其也能获取到用户上下文认证信息。
[SecurityContextPersistenceFilter] (同步方式)在请求之前从SecurityContextRepository(默认实现是HttpSessionSecurityContextRepository)获取信息并填充SecurityContextHolder(如果没有,则创建一个新的ThreadLocal的SecurityContext),并在请求完成并清空SecurityContextHolder并更新SecurityContextRepository。
在Spring Security中,虽然安全上下文信息被存储于Session中,但实际的Filter中不应直接操作Session(过滤器一般负责核心的处理流程,而具体的业务实现,通常交给其中聚合的其他实体类),而是用如HttpSessionSecurityContextRepository中loadContext(),saveContext()来存取session。
[HeaderWriterFilter] 用来给http响应添加一些Header,比如X-Frame-Options,X-XSS-Protection*,X-Content-Type-Options。
[CsrfFilter] 默认开启,用于防止csrf攻击的过滤器
[LogoutFilter] 处理注销的过滤器
[UsernamePasswordAuthenticationFilter] 表单提交了username和password,被封装成UsernamePasswordAuthenticationToken对象进行一系列的认证,便是主要通过这个过滤器完成的,即调用AuthenticationManager.authenticate()。在表单认证的方法中,这是最最关键的过滤器。具体过程是:
(1)调用AbstractAuthenticationProcessingFilter.doFilter()方法执行过滤器
(2)调用UsernamePasswordAuthenticationFilter.attemptAuthentication()方法
(3)调用AuthenticationManager.authenticate()方法(实际上委托给AuthenticationProvider的实现类来处理)
[DefaultLoginPageGeneratingFilter] & [DefaultLogoutPageGeneratingFilter] 如果没有配置/login及login page, 系统则会自动配置这两个Filter。
[BasicAuthenticationFilter] Processes a HTTP request's BASIC authorization headers, putting the result into the SecurityContextHolder.
[RequestCacheAwareFilter] 内部维护了一个RequestCache,用于缓存request请求
[SecurityContextHolderAwareRequestFilter] 此过滤器对ServletRequest进行了一次包装,使得request具有更加丰富的API(populates the ServletRequest with a request wrapper which implements servlet API security methods)
[AnonymousAuthenticationFilter] 匿名身份过滤器,spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。它位于身份认证过滤器(e.g. UsernamePasswordAuthenticationFilter)之后,意味着只有在上述身份过滤器执行完毕后,SecurityContext依旧没有用户信息,AnonymousAuthenticationFilter该过滤器才会有意义。
[SessionManagementFilter] 和session相关的过滤器,内部维护了一个SessionAuthenticationStrategy来执行任何与session相关的活动,比如session-fixation protection mechanisms or checking for multiple concurrent logins。
[ExceptionTranslationFilter] 异常转换过滤器,这个过滤器本身不处理异常,而是将认证过程中出现的异常(AccessDeniedException and AuthenticationException)交给内部维护的一些类去处理。它
位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常,将其转化,顾名思义,转化以意味本身并不处理。一般其只处理两大类异常:AccessDeniedException访问异常和AuthenticationException认证异常。
它将Java中的异常和HTTP的响应连接在了一起,这样在处理异常时,我们不用考虑密码错误该跳到什么页面,账号锁定该如何,只需要关注自己的业务逻辑,抛出相应的异常便可。如果该过滤器检测到AuthenticationException,则将会交给内部的AuthenticationEntryPoint去处理,如果检测到AccessDeniedException,需要先判断当前用户是不是匿名用户,如果是匿名访问,则和前面一样运行AuthenticationEntryPoint,否则会委托给AccessDeniedHandler去处理,而AccessDeniedHandler的默认实现,是AccessDeniedHandlerImpl。
[FilterSecurityInterceptor] 这个过滤器决定了访问特定路径应该具备的权限,这些受限的资源访需要什么权限或角色,这些判断和处理都是由该类进行的。
(1)调用FilterSecurityInterceptor.invoke()方法执行过滤器
(2)调用AbstractSecurityInterceptor.beforeInvocation()方法
(3)调用AccessDecisionManager.decide()方法决策判断是否有该权限