目录
AnyRequestMatcher
WebSecurityConfig
HttpSecurity
AbstractInterceptUrlConfigurer
AbstractAuthenticationProcessingFilter
类图
在前面的文章《spring-security原理与应用系列:securityFilterChainBuilders》中,我们遗留了一个问题:HttpSecurity建造者的build()方法最终构建的是DefaultSecurityFilterChain对象,该对象的两个构造参数requestMatcher和filters是如何初始化的呢?
这篇文章,我们来了解其中跟filters对象相关的流程。
设置断点,如下所示:
在这里, 我们看到requestMatcher对象的类型是AnyRequestMatcher,如下所示:
AnyRequestMatcher
public final class AnyRequestMatcher implements RequestMatcher {
public static final RequestMatcher INSTANCE = new AnyRequestMatcher();
public boolean matches(HttpServletRequest request) {
return true;
}
在这里,matches()方法直接返回true,所以自定义配置的请求地址串也就没有放在这里了。
所以,这个filters列表里所有的过滤器都会执行,这个与核心过滤器的filters对象有较大的区别。
请求地址的匹配操作会在filters里的每个过滤器都进行一次过滤(除非中间执行过程中有不满足过滤器要求而提前结束HTTP请求)。
所以,我们重点关注filters的对象即可。
我们的自定义配置类如下所示:
WebSecurityConfig
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecurityConfig securityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 配置白名单(比如登录接口)
.antMatchers(securityConfig.getPermitUrls()).permitAll()
// 匿名访问的URL,即不用登录也可以访问(比如广告接口)
.antMatchers(securityConfig.getAnonymousUrls()).permitAll()
// 买家接口需要 “ROLE_BUYER” 角色权限才能访问
.antMatchers("/buyer/**").hasRole("BUYER")
// 其他任何请求满足 rbacService.hasPermission() 方法返回true时,能够访问
.anyRequest().access("@rbacService.hasPermission(request, authentication)")
... ...}
点击authorizeRequests(),如下所示:
HttpSecurity
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>(context))
.getRegistry();
}
在这里,将配置器ExpressionUrlAuthorizationConfigurer使用于建造者HttpSecurity中。
通过前面的章节,我们知道,建造者在建造对象时,会调用配置器的configure()方法,找到配置器ExpressionUrlAuthorizationConfigurer的configure()方法,如下所示:
AbstractInterceptUrlConfigurer
abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<C, H> {
private Boolean filterSecurityInterceptorOncePerRequest;
private AccessDecisionManager accessDecisionManager;
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if (filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor
.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}
在这里,果不其然,我们看到了往建造者HttpSecurity对象添加过滤器filter的代码http.addFilter(securityInterceptor)。该过滤器的类型是FilterSecurityInterceptor。
通过上面的分析,我们知道,请求地址的匹配操作会在filters里的每个过滤器都进行一次过滤(除非中间执行过程中有不满足过滤器要求而提前结束HTTP请求的)。
接下来,我们找一个比较简单的过滤器,了解一下这些过滤器是如何进行请求地址匹配的,如下所示:
AbstractAuthenticationProcessingFilter
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
... ...
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
unsuccessfulAuthentication(request, response, failed);
return;
}
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
在这里,通过调用!requiresAuthentication(request, response)判断是否需要对当前请求进行认证,不需要则执行下一个过滤器,需要则进行认证操作。
点击requiresAuthentication()方法,如下所示:
protected boolean requiresAuthentication(HttpServletRequest request,
HttpServletResponse response) {
return requiresAuthenticationRequestMatcher.matches(request);
}
spring-security还内置了许多的过滤器,如下所示:
这些过滤器给我们的开发工作带来了你很大的方便,减少了大量的工作,这个后续我们再探究。
如下是与过滤器FilterSecurityInterceptor相关的类图:
类图
这里是与过滤器FilterSecurityInterceptor的Configurer相关的类图。
这里是配置器的属性ExpressionInterceptUrlRegistry相关的类图。
这里是过滤器FilterSecurityInterceptor相关的类图。这个过滤器比较复杂与比较重要,我们后续再专门开一篇文章进行探究。