关于java:深入理解-SecurityConfigurer-源码篇

41次阅读

共计 10270 个字符,预计需要花费 26 分钟才能阅读完成。

咱们来持续撸 Spring Security 源码。

SecurityConfigurer 在 Spring Security 中是一个十分重要的角色。在后面的文章中,松哥已经屡次提到过,Spring Security 过滤器链中的每一个过滤器,都是通过 xxxConfigurer 来进行配置的,而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。

所以咱们明天有必要来跟大家把 SecurityConfigurer 从头到尾捋一捋。

1. SecurityConfigurer

SecurityConfigurer 自身是一个接口,咱们来看下:

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {void init(B builder) throws Exception;

    void configure(B builder) throws Exception;
}

能够看到,SecurityConfigurer 中次要是两个办法,init 和 configure。

init 就是一个初始化办法。而 configure 则是一个配置办法。这里只是标准了办法的定义,具体的实现则在不同的实现类中。

须要留神的是这两个办法的参数类型都是一个泛型 B,也就是 SecurityBuilder 的子类,对于 SecurityBuilder,它是用来构建过滤器链的,松哥将在下篇文章中和大家介绍。

SecurityConfigurer 有三个实现类:

  • SecurityConfigurerAdapter
  • GlobalAuthenticationConfigurerAdapter
  • WebSecurityConfigurer

咱们别离来看。

1.1 SecurityConfigurerAdapter

SecurityConfigurerAdapter 实现了 SecurityConfigurer 接口,咱们所应用的大部分的 xxxConfigurer 也都是 SecurityConfigurerAdapter 的子类。

SecurityConfigurerAdapter 在 SecurityConfigurer 的根底上,还扩大进去了几个十分好用的办法,咱们一起来看下:

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
        implements SecurityConfigurer<O, B> {
    private B securityBuilder;

    private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();

    public void init(B builder) throws Exception { }

    public void configure(B builder) throws Exception { }

    public B and() {return getBuilder();
    }
    protected final B getBuilder() {if (securityBuilder == null) {throw new IllegalStateException("securityBuilder cannot be null");
        }
        return securityBuilder;
    }
    @SuppressWarnings("unchecked")
    protected <T> T postProcess(T object) {return (T) this.objectPostProcessor.postProcess(object);
    }
    public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
    }
    public void setBuilder(B builder) {this.securityBuilder = builder;}
    private static final class CompositeObjectPostProcessor implements
            ObjectPostProcessor<Object> {private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();

        @SuppressWarnings({"rawtypes", "unchecked"})
        public Object postProcess(Object object) {for (ObjectPostProcessor opp : postProcessors) {Class<?> oppClass = opp.getClass();
                Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,
                        ObjectPostProcessor.class);
                if (oppType == null || oppType.isAssignableFrom(object.getClass())) {object = opp.postProcess(object);
                }
            }
            return object;
        }
        private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {boolean result = this.postProcessors.add(objectPostProcessor);
            postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
            return result;
        }
    }
}
  1. CompositeObjectPostProcessor 首先一开始申明了一个 CompositeObjectPostProcessor 实例,CompositeObjectPostProcessor 是 ObjectPostProcessor 的一个实现,ObjectPostProcessor 自身是一个后置处理器,该后置处理器默认有两个实现,AutowireBeanFactoryObjectPostProcessor 和 CompositeObjectPostProcessor。其中 AutowireBeanFactoryObjectPostProcessor 次要是利用了 AutowireCapableBeanFactory 对 Bean 进行手动注册,因为在 Spring Security 中,很多对象都是手动 new 进去的,这些 new 进去的对象和容器没有任何关系,利用 AutowireCapableBeanFactory 能够将这些手动 new 进去的对象注入到容器中,而 AutowireBeanFactoryObjectPostProcessor 的次要作用就是实现这件事;CompositeObjectPostProcessor 则是一个复合的对象处理器,里边保护了一个 List 汇合,这个 List 汇合中,大部分状况下只存储一条数据,那就是 AutowireBeanFactoryObjectPostProcessor,用来实现对象注入到容器的操作,如果用户本人手动调用了 addObjectPostProcessor 办法,那么 CompositeObjectPostProcessor 汇合中保护的数据就会多进去一条,在 CompositeObjectPostProcessor#postProcess 办法中,会遍历汇合中的所有 ObjectPostProcessor,挨个调用其 postProcess 办法对对象进行后置解决。
  2. and 办法,该办法返回值是一个 securityBuilder,securityBuilder 实际上就是 HttpSecurity,咱们在 HttpSecurity 中去配置不同的过滤器时,能够应用 and 办法进行链式配置,就是因为这里定义了 and 办法并返回了 securityBuilder 实例。

这便是 SecurityConfigurerAdapter 的次要性能,前面大部分的 xxxConfigurer 都是基于此类来实现的。

1.2 GlobalAuthenticationConfigurerAdapter

GlobalAuthenticationConfigurerAdapter 看名字就晓得是一个跟全局配置无关的货色,它自身实现了 SecurityConfigurerAdapter 接口,然而并未对办法做具体的实现,只是将泛型具体化了:

@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter implements
        SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {public void init(AuthenticationManagerBuilder auth) throws Exception { }

    public void configure(AuthenticationManagerBuilder auth) throws Exception {}}

能够看到,SecurityConfigurer 中的泛型,当初明确成了 AuthenticationManager 和 AuthenticationManagerBuilder。所以 GlobalAuthenticationConfigurerAdapter 的实现类未来次要是和配置 AuthenticationManager 无关。当然也包含默认的用户名明码也是由它的实现类来进行配置的。

咱们在 Spring Security 中应用的 AuthenticationManager 其实能够分为两种,一种是部分的,另一种是全局的,这里次要是全局的配置。

1.3 WebSecurityConfigurer

还有一个实现类就是 WebSecurityConfigurer,这个可能有的小伙伴比拟生疏,其实他就是咱们天天用的 WebSecurityConfigurerAdapter 的父接口。

所以 WebSecurityConfigurer 的作用就很明确了,用户扩大用户自定义的配置。

SecurityConfigurer 默认次要是这三个实现,思考到大多数的过滤器配置都是通过 SecurityConfigurerAdapter 进行扩大的,因而咱们明天就通过这条线进行开展。另外两条线松哥也将撸两篇文章和大家介绍。

2. SecurityConfigurerAdapter

SecurityConfigurerAdapter 的实现次要也是三大类:

  • UserDetailsAwareConfigurer
  • AbstractHttpConfigurer
  • LdapAuthenticationProviderConfigurer

思考到 LDAP 当初应用很少,所以这里我来和大家重点介绍下前两个。

2.1 UserDetailsAwareConfigurer

这个配置类看名字大略就晓得这是用来配置用户类的。

AbstractDaoAuthenticationConfigurer

AbstractDaoAuthenticationConfigurer 中所做的事件比较简单,次要是结构了一个默认的 DaoAuthenticationProvider,并为其配置 PasswordEncoder 和 UserDetailsService。

UserDetailsServiceConfigurer

UserDetailsServiceConfigurer 重写了 AbstractDaoAuthenticationConfigurer 中的 configure 办法,在 configure 办法执行之前退出了 initUserDetailsService 办法,以不便开倒退依照本人的形式去初始化 UserDetailsService。不过这里的 initUserDetailsService 办法是空办法。

UserDetailsManagerConfigurer

UserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的 initUserDetailsService 办法,具体的实现逻辑就是将 UserDetailsBuilder 所构建进去的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService 中。

该类同时增加了 withUser 办法用来增加用户,同时还减少了一个 UserDetailsBuilder 用来构建用户,这些逻辑都比较简单,小伙伴们能够自行查看。

JdbcUserDetailsManagerConfigurer

JdbcUserDetailsManagerConfigurer 在父类的根底上补充了 DataSource 对象,同时还提供了相应的数据库查询方法。

InMemoryUserDetailsManagerConfigurer

InMemoryUserDetailsManagerConfigurer 在父类的根底上重写了构造方法,将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManager。

DaoAuthenticationConfigurer

DaoAuthenticationConfigurer 继承自 AbstractDaoAuthenticationConfigurer,只是在构造方法中批改了一下 userDetailsService 而已。

有小伙伴可能要问了,JdbcUserDetailsManagerConfigurer 或者 InMemoryUserDetailsManagerConfigurer,到底在哪里能够用到呀?

松哥给大家举一个简略的例子:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("javaboy")
                .password("{noop}123")
                .roles("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()
                .anyRequest().authenticated()
                // 省略
    }
}

当你调用 auth.inMemoryAuthentication 进行配置时,实际上调用的就是 InMemoryUserDetailsManagerConfigurer。

这下明确了吧!

2.2 AbstractHttpConfigurer

AbstractHttpConfigurer 这一派中的货色十分多,咱们所有的过滤器配置,都是它的子类,咱们来看下都有哪些类?

能够看到,它的实现类还是十分多的。

这么多实现类,松哥就不一一给大家介绍了,我挑一个罕用的 FormLoginConfigurer 来给大家具体介绍,只有大家把这个了解了,其余的照猫画虎就很好了解了。

咱们一个一个来看。

2.2.1 AbstractHttpConfigurer

AbstractHttpConfigurer 继承自 SecurityConfigurerAdapter,并减少了两个办法,disable 和 withObjectPostProcessor:

public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
        extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {

    /**
     * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
     * version of the configuration can be applied.
     *
     * @return the {@link HttpSecurityBuilder} for additional customizations
     */
    @SuppressWarnings("unchecked")
    public B disable() {getBuilder().removeConfigurer(getClass());
        return getBuilder();}

    @SuppressWarnings("unchecked")
    public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {addObjectPostProcessor(objectPostProcessor);
        return (T) this;
    }
}

这两个办法松哥之前都有给大家介绍过,disable 基本上是大家的老熟人了,咱们罕用的 .csrf().disable() 就是出自这里,那么从这里咱们也能够看到 disable 的实现原理,就是从 getBuilder 中移除相干的 xxxConfigurer,getBuilder 办法获取到的实际上就是 HttpSecurity,所以移除掉 xxxConfigurer 实际上就是从过滤器链中移除掉某一个过滤器,例如 .csrf().disable() 就是移除掉解决 csrf 的过滤器。

另一个减少的办法是 withObjectPostProcessor,这是为配置类增加手动增加后置处理器的。在 AbstractHttpConfigurer 的父类中其实有一个相似的办法就是 addObjectPostProcessor,然而 addObjectPostProcessor 只是一个增加办法,返回值为 void,而 withObjectPostProcessor 的返回值是以后配置类,也就是 xxxConfigurer,所以如果应用 withObjectPostProcessor 的话,能够应用链式配置,事实上,在松哥之前的文章,以及 vhr(https://github.com/lenve/vhr) 我的项目中,应用的也都是 withObjectPostProcessor 办法(当然,你也能够应用 addObjectPostProcessor,最终成果是一样的)。

2.2.2 AbstractAuthenticationFilterConfigurer

AbstractAuthenticationFilterConfigurer 类的性能比拟多,源码也是相当相当长。不过咱们只须要抓住两点即可,init 办法和 configure 办法,因为这两个办法是所有 xxxConfigurer 的灵魂。

@Override
public void init(B http) throws Exception {updateAuthenticationDefaults();
    updateAccessDefaults(http);
    registerDefaultAuthenticationEntryPoint(http);
}

init 办法次要干了三件事:

  1. updateAuthenticationDefaults 次要是配置了登录解决地址,失败跳转地址,登记胜利跳转地址。
  2. updateAccessDefaults 办法次要是对 loginPage、loginProcessingUrl、failureUrl 进行 permitAll 设置(如果用户配置了 permitAll 的话)。
  3. registerDefaultAuthenticationEntryPoint 则是注册异样的处理器。

再来看 configure 办法:

@Override
public void configure(B http) throws Exception {PortMapper portMapper = http.getSharedObject(PortMapper.class);
    if (portMapper != null) {authenticationEntryPoint.setPortMapper(portMapper);
    }
    RequestCache requestCache = http.getSharedObject(RequestCache.class);
    if (requestCache != null) {this.defaultSuccessHandler.setRequestCache(requestCache);
    }
    authFilter.setAuthenticationManager(http
            .getSharedObject(AuthenticationManager.class));
    authFilter.setAuthenticationSuccessHandler(successHandler);
    authFilter.setAuthenticationFailureHandler(failureHandler);
    if (authenticationDetailsSource != null) {authFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
    }
    SessionAuthenticationStrategy sessionAuthenticationStrategy = http
            .getSharedObject(SessionAuthenticationStrategy.class);
    if (sessionAuthenticationStrategy != null) {authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
    }
    RememberMeServices rememberMeServices = http
            .getSharedObject(RememberMeServices.class);
    if (rememberMeServices != null) {authFilter.setRememberMeServices(rememberMeServices);
    }
    F filter = postProcess(authFilter);
    http.addFilter(filter);
}

configure 中的逻辑就很简答了,构建各种各样的回调函数设置给 authFilter,authFilter 再去 postProcess 中走一圈注册到 Spring 容器中,最初再把 authFilter 增加到过滤器链中。

这便是 AbstractAuthenticationFilterConfigurer 的次要性能。须要揭示大家的是,咱们日常配置的,如:

  • loginPage
  • loginProcessingUrl
  • permitAll
  • defaultSuccessUrl
  • failureUrl

等办法都是在这里定义的。

最初咱们再来看看 FormLoginConfigurer。

2.2.3 FormLoginConfigurer

FormLoginConfigurer 在定义是,明确了 AbstractAuthenticationFilterConfigurer 中的泛型是 UsernamePasswordAuthenticationFilter,也就是咱们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter。

FormLoginConfigurer 重写了 init 办法,配置了一下默认的登录页面。其余的基本上都是从父类来的,未做太多扭转。

另外咱们日常配置的很多货色也是来自这里:

好啦,这就是 FormLoginConfigurer 这个配置类,FormLoginConfigurer 对应的过滤器是 UsernamePasswordAuthenticationFilter,小伙伴们能够自行剖析其余的 xxxConfigurer,每一个 xxxConfigurer 都对应了一个 不同的 Filter。

3. 小结

好啦,明天就次要和大家分享一下 SecurityConfigurer 的源码,当然这里还有很多值得再次认真探讨的货色,松哥将在前面的文章中持续和大家分享。

感觉有播种的小伙伴记得点个在看激励下松哥哦~

正文完
 0