雨纷纷,旧故里草木深,我听闻,你仍守着孤城。
一、默认拦截策略
1.1 Spring Security自动配置SecurityFilterChain的默认拦截策略
我们先回顾下Spring Security自动配置SecurityFilterChain:
从类注释上可以看出来,这个配置是在用户没有配置SecurityFilterChain时才会加载,为什么呢,我们看下类上的注解@ConditionalOnDefaultWebSecurity,看下这个注解内部:
继续跟进@Conditional(DefaultWebSecurityCondition.class):
这里我们可以看出来,需要加载默认的SecurityFilterChain的条件时没有其他的SecurityFilterChain类型的对象,因此当我们自定义后将不会加载默认配置。本章节我们只讨论默认配置,不自定义SecurityFilterChain。
默认配置的第一行就是配置了所有请求都必须要通过认证,下面我们来详细研究一下:
http.authorizeRequests().anyRequest().authenticated();
进入authorizeRequests():
这里我们前面已经详细的说明过了,创建了一个配置器ExpressionUrlAuthorizationConfigurer,并且放入了httpSecurity的配置器集合configurers中,最终在httpSecurity执行构建方法时,会调用到配置器的configure方法,因此我们这里直接看这个配置器的configure方法:
注意看这里也是创建了一个过滤器FilterSecurityInterceptor,这个过滤器在什么时候执行呢,让我们回到前面说过的httpSecurity中的属性filterOrders,这个属性时默认有初始化动作的,这些我们前面都已经说明过了,这里再注意看下上面这个过滤器初始化的顺序在哪里:
倒数第三行,可以说非常靠后了,而且倒数第二行的过滤器AuthorizationFilter和我们这个过滤器是不会同时存在的,后面我们自定义配置的时候就能看到,同时这里说明一下当下我们自定义SecurityFilterChain时基本都是配置AuthorizationFilter这个过滤器了,FilterSecurityInterceptor这个过滤器可以考虑不再使用了。所以这里我们默认配置的这个过滤器不讨论的过于详细了,大概说下原理。言归正传,再看后面两个方法,首先anyRequest(),是创建了一个AnyRequestMatcher,随后作为参数创建了一个AuthorizedUrl:
随后调用AuthorizedUrl的authenticated()方法:
这里创建的urlMappings路径映射集合,在配置器完成初始化、配置等操作后会在FilterSecurityInterceptor拦截请求的时候使用,这中间的过程感兴趣的同学可以自己研究下,因为后面我们自己配置的时候使用另一个过滤器,这里不详细展开,直接看下这个过滤器的执行逻辑:
进入super.beforeInvocation方法:
上面就是大概的一个过滤器拦截流程,在执行完所有前置过滤器进入这个FilterSecurityInterceptor过滤器后,最后通过访问决策管理器验证Authentication是否放行。这里说明下,这个旧的配置器初始化、配置流程比较复杂,然后在过滤器中使用访问决策管理器来验证是否放行请求的逻辑也比较复杂,因此现在不推荐使用这个配置器,我们自定义配置的时候也不推荐使用这个配置器,后面我们详细说明,这里只做一个总结,Spring Security是如何放行一个请求的大概原理,不做详细说明。
总结一下:
无论是使用旧的配置器还是新的配置器,Spring Security都是在最后一个过滤器中验证是否放行请求,而放行请求的条件就是:
- 在前面过滤器中任意一个过滤器完成了认证,生成一个已认证的Authentication
- 在到达最后一个过滤器时,传入的Authentication未认证,但是对请求url配置了放行策略
二、Spring Security自定义拦截策略
2.1 自定义配置
上面我们说明了,Spring Security默认给我们配置的是FilterSecurityInterceptor过滤器做最后的是否放行判断,实际使用中我们通常都会自定义配置,且会配置成AuthorizationFilter作为最后是否放行判断的过滤器,现在开始我们开始自己配置了,首先配置拦截策略:
创建配置器:
看看这个配置器的配置:
这里会发现配置器会帮助我们创建过滤器AuthorizationFilter,还记不记得HttpSecurity中配置的过滤器的顺序?前面已经有说明过了,这里再看一眼,FilterOrderRegistration类中:
可以说已经在最后了,因此自定义配置完这个拦截策略后,AuthorizationFilter将担任最后决定是否放行请求的角色。我们再来看看这个过滤器中是如何进行判断的。
首先看看这个过滤器初始化做了哪些事情,先看这个:
anyRequest()方法先生成一个AnyRequestMatcher对象,并将其作为参数传入AuthorizedUrl类的构造方法从而创建了一个AuthorizedUrl对象:
然后调用authenticated()方法:
access()方法里的manager是AuthenticatedAuthorizationManager对象,再往下:
记住这个builder是AuthorizeHttpRequestsConfigurer配置器里registry属性对象里的一个属性,下面我们配置器在配置的时候会使用到这个属性;先总结一下:
- 首先,我们自定义配置拦截策略是通过AuthorizeHttpRequestsConfigurer配置器来进行配置的;
- AuthorizeHttpRequestsConfigurer配置器初始化的时候我们定义了一个AnyRequestMatcher和一个AuthenticatedAuthorizationManager对象,并保存在配置器的registry属性中。
记住上面的初始化内容,我们接下来再看下配置器的配置:
创建了一个委派请求匹配管理器,并作为参数传入AuthorizationFilter中,也就是AuthorizationFilter中的authorizationManager属性:
到这里,自定义拦截策略大概流程就结束了,我们在来看下AuthorizationFilter是怎么工作的,首先进入过滤器后直接看authorizationManager的check方法,我们要记号上面配置器初始化的时候做的事情,check方法进入下面的方法中:
遍历这个mappings进行匹配,当请求url被匹配上之后就会执行manager的check方法来决定请求是否被放行。
那么基于我们当前的这个配置,这个mappings还记得吗?mappings只有一个元素,这个元素是由AnyRequestMatcher和AuthenticatedAuthorizationManager作为参数创建的RequestMatcherEntry对象,而AnyRequestMatcher会匹配上所有请求:
然后进入AuthenticatedAuthorizationManager的check方法:
这里我们可以看到,只有在不是匿名且已经认证通过的请求会被放行。到这里,我们的自定义拦截策略流程已经介绍完了,怎么样,AuthorizationFilter相比较于FilterSecurityInterceptor是不是更加容易理解,逻辑更加清晰呢。
再总结一下:
- anyRequest()方法会匹配到所有请求,authenticated()方法只放行非匿名且已认证通过的请求。
- 基于我们上面的介绍,我们还可以自定义一些放行策略,指定哪些请求放行,哪些请求需要什么权限才放行,后面我们会慢慢看到的,相信使用过Spirng Security的同学一定是知道的。下一章节我们开始介绍表单登录。
未完待续~