史上最简单的Spring Security教程(十七):FilterSecurityInterceptor默认初始化逻辑剖析

 

Spring Security 框架默认会自动创建一个 FilterSecurityInterceptor 实例,如我们前面所述,自定义的 FilterSecurityInterceptor 要么是在默认 FilterSecurityInterceptor 实例之前,要么是在之后,看具体业务场景。为啥我们不能替换掉默认的 FilterSecurityInterceptor 实例呢?先来剖析一下默认的 FilterSecurityInterceptor 实例初始化逻辑。

SecurityMetadataSource

 

Spring Security 主要通过如下方法来配置默认的 FilterSecurityInterceptor 实例的 SecurityMetadataSource

http
  .authorizeRequests()
    .anyRequest()
    .authenticated()
    .antMatchers().permitAll()
    .antMatchers().hasRole()
    .antMatchers().hasAuthority()
  ......

 

怎么初始化的呢?

首先,在 AbstractInterceptUrlConfigurer 类的 configure 方法中定义实例。

@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);
}

 

同样的,该实例也会放到 sharedObject 中。

FilterSecurityInterceptor 实例的创建是调用的 AbstractInterceptUrlConfigurer 类的 createFilterSecurityInterceptor 方法,创建逻辑如下:

private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,
      FilterInvocationSecurityMetadataSource metadataSource,
      AuthenticationManager authenticationManager) throws Exception {
    FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
    securityInterceptor.setSecurityMetadataSource(metadataSource);
    securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
    securityInterceptor.setAuthenticationManager(authenticationManager);
    securityInterceptor.afterPropertiesSet();
    return securityInterceptor;
}

 

SecurityMetadataSource 是通过 AbstractInterceptUrlConfigurer 类的抽象 createMetadataSource 方法来创建。

/**
 * Subclasses should implement this method to provide a
 * {@link FilterInvocationSecurityMetadataSource} for the
 * {@link FilterSecurityInterceptor}.
 *
 * @param http the builder to use
 *
 * @return the {@link FilterInvocationSecurityMetadataSource} to set on the
 * {@link FilterSecurityInterceptor}. Cannot be null.
 */
abstract FilterInvocationSecurityMetadataSource createMetadataSource(H http);

 

其具体逻辑,是由 AbstractInterceptUrlConfigurer 类的子类 ExpressionUrlAuthorizationConfigurer 提供。为什么是这个类呢?看看最前面我们说的如何配置 FilterSecurityInterceptor 实例的 SecurityMetadataSource,就能基本明白。

@Override
final ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(
    H http) {
    LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = REGISTRY
        .createRequestMap();
    if (requestMap.isEmpty()) {
        throw new IllegalStateException(
            "At least one mapping is required (i.e. authorizeRequests().anyRequest().authenticated())");
    }
    return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap,
                                                                     getExpressionHandler(http));
}

 

REGISTRY 是什么呢?正是 ExpressionInterceptUrlRegistry。可以看看 http.authorizeRequests() 返回值类型,正是 ExpressionInterceptUrlRegistry。而该类,正是 ExpressionUrlAuthorizationConfigurer 类的子类。

public class ExpressionInterceptUrlRegistry
      extends
      ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry, AuthorizedUrl>

 

现在,应该基本明白前面的配置是做什么的了吧?那再来看一下 requestMap 是如何创建的。requestMap 是由抽象类 AbstractConfigAttributeRequestMatcherRegistry 创建的。这个抽象类是 AbstractInterceptUrlRegistry 类的基类,而 AbstractInterceptUrlRegistry 类,正是 ExpressionInterceptUrlRegistry 类的基类。

final LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> createRequestMap() {
    if (unmappedMatchers != null) {
        throw new IllegalStateException(
            "An incomplete mapping was found for "
            + unmappedMatchers
            + ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
    }
    LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
    for (UrlMapping mapping : getUrlMappings()) {
        RequestMatcher matcher = mapping.getRequestMatcher();
        Collection<ConfigAttribute> configAttrs = mapping.getConfigAttrs();
        requestMap.put(matcher, configAttrs);
    }
    return requestMap;
}

 

正是把前面 http 配置的 authorizeRequests,转化为 UrlMappings,然后再转换为 LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>。

而 ExpressionUrlAuthorizationConfigurer 类的 interceptUrl,正是向 UrlMappings 中添加内容。

SecurityMetadataSource 的初始化基本完成,下面再来说一下 AccessDecisionManager 的初始化过程。

 

AccessDecisionManager

 

默认的 AccessDecisionManager 初始化也是由 AbstractInterceptUrlConfigurer 类创建的。

/**
 * Creates the default {@code AccessDecisionManager}
 * @return the default {@code AccessDecisionManager}
 */
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
    AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
    return postProcess(result);
}
/**
 * If currently null, creates a default {@link AccessDecisionManager} using
 * {@link #createDefaultAccessDecisionManager(HttpSecurityBuilder)}. Otherwise returns the
 * {@link AccessDecisionManager}.
 *
 * @param http the builder to use
 *
 * @return the {@link AccessDecisionManager} to use
 */
private AccessDecisionManager getAccessDecisionManager(H http) {
    if (accessDecisionManager == null) {
        accessDecisionManager = createDefaultAccessDecisionManager(http);
    }
    return accessDecisionManager;
}

 

如果没有设置自定义的 accessDecisionManager,则会创建默认的 AffirmativeBased 实例。

 

AccessDecisionVoter

 

接下来,说一下 AccessDecisionVoter 初始化过程。AccessDecisionVoters 也是由AbstractInterceptUrlConfigurer 类的抽象方法 getDecisionVoters 提供。

/**
 * Subclasses should implement this method to provide the {@link AccessDecisionVoter}
 * instances used to create the default {@link AccessDecisionManager}
 *
 * @param http the builder to use
 *
 * @return the {@link AccessDecisionVoter} instances used to create the default
 * {@link AccessDecisionManager}
 */
abstract List<AccessDecisionVoter<? extends Object>> getDecisionVoters(H http);

 

而真正的逻辑实现,是由其子类 ExpressionUrlAuthorizationConfigurer 提供。

@Override
@SuppressWarnings("rawtypes")
final List<AccessDecisionVoter<? extends Object>> getDecisionVoters(H http) {
    List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
    WebExpressionVoter expressionVoter = new WebExpressionVoter();
    expressionVoter.setExpressionHandler(getExpressionHandler(http));
    decisionVoters.add(expressionVoter);
    return decisionVoters;
}

 

可以看到,decisionVoters 只有 WebExpressionVoter 实例。

 

ExpressionHandler

 

最后,来说一下 ExpressionHandler 的初始化逻辑。ExpressionHandler 的初始化逻辑是由 ExpressionUrlAuthorizationConfigurer 类的 getExpressionHandler 方法实现。

private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
    if (expressionHandler == null) {
        DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
        AuthenticationTrustResolver trustResolver = http
            .getSharedObject(AuthenticationTrustResolver.class);
        if (trustResolver != null) {
            defaultHandler.setTrustResolver(trustResolver);
        }
        ApplicationContext context = http.getSharedObject(ApplicationContext.class);
        if (context != null) {
            String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
            if (roleHiearchyBeanNames.length == 1) {
                defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));
            }
            String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
            if (grantedAuthorityDefaultsBeanNames.length == 1) {
                GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
                defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
            }
            String[] permissionEvaluatorBeanNames = context.getBeanNamesForType(PermissionEvaluator.class);
            if (permissionEvaluatorBeanNames.length == 1) {
                PermissionEvaluator permissionEvaluator = context.getBean(permissionEvaluatorBeanNames[0], PermissionEvaluator.class);
                defaultHandler.setPermissionEvaluator(permissionEvaluator);
            }
        }
        expressionHandler = postProcess(defaultHandler);
    }
    return expressionHandler;
}

 

首先,expressionHandler 是 DefaultWebSecurityExpressionHandler 实例。

如果存在 AuthenticationTrustResolver 实例,则设置到 DefaultWebSecurityExpressionHandler 实例中。

如果存在 RoleHierarchy 实例(隶属关系角色),同样设置到 DefaultWebSecurityExpressionHandler 实例中。

如果存在 GrantedAuthorityDefaults 实例(设置角色前缀的类),则设置该实例中定义的角色前缀到 DefaultWebSecurityExpressionHandler 实例中。

如果存在 PermissionEvaluator 实例,同样设置到 DefaultWebSecurityExpressionHandler 实例中。

PermissionEvaluator 用于确定用户是否具有权限或给定域对象的权限。

 

 

源码

 

github

 

https://github.com/liuminglei/SpringSecurityLearning/tree/master/17

 

gitee

 

https://gitee.com/xbd521/SpringSecurityLearning/tree/master/17

 

 

 

 

回复以下关键字,获取更多资源

 

SpringCloud进阶之路 | Java 基础 | 微服务 | JAVA WEB | JAVA 进阶 | JAVA 面试 | MK 精讲

 

 

 

笔者开通了个人微信公众号【银河架构师】,分享工作、生活过程中的心得体会,填坑指南,技术感悟等内容,会比博客提前更新,欢迎订阅。

技术资料领取方法:关注公众号,回复微服务,领取微服务相关电子书;回复MK精讲,领取MK精讲系列电子书;回复JAVA 进阶,领取JAVA进阶知识相关电子书;回复JAVA面试,领取JAVA面试相关电子书,回复JAVA WEB领取JAVA WEB相关电子书。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值