
一、SecurityConfigurerSecurityConfigurer 在 Spring Security 中是一个非常重要的角色。在前面的内容中曾经多次提到过 Spring Security 过滤器链中的每一个过滤器都是通过 xxxConfigurer 来进行配置的而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。所以我们也需要对 SecurityConfigurer 理解清楚.// ODefaultSecurityFilterChain BHttpSecurity public interface SecurityConfigurerO, B extends SecurityBuilderO { /** * Initialize the {link SecurityBuilder}. Here only shared state should be created * and modified, but not properties on the {link SecurityBuilder} used for building * the object. This ensures that the {link #configure(SecurityBuilder)} method uses * the correct shared objects when building. Configurers should be applied here. * param builder * throws Exception */ void init(B builder) throws Exception; /** * Configure the {link SecurityBuilder} by setting the necessary properties on the * {link SecurityBuilder}. * param builder * throws Exception */ void configure(B builder) throws Exception; }在init方法和configure方法中的形参都是SecurityBuilder类型而SecurityBuilder是用来构建过滤器链 的【DefaultSecurityFilterChainProxy】 SecurityConfigurer的实现当然很多。具体的Configurer实现我们可以先不关注我需要了解的SecurityConfigurer的核心实现有三个二、SecurityConfigurerAdapterSecurityConfigurerAdapter 实现了 SecurityConfigurer 接口我们所使用的大部分的xxxConfigurer 也都是 SecurityConfigurerAdapter 的子类。 SecurityConfigurerAdapter 在 SecurityConfigurer 的基础上还扩展出来了几个非常好用的方法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 方法对对象进行后置处理。and () 该方法返回值是一个 securityBuildersecurityBuilder 实际上就是 HttpSecurity我们在HttpSecurity 中去配置不同的过滤器时可以使用 and 方法进行链式配置就是因为这里定义了 and 方法并返回了 securityBuilder 实例这便是 SecurityConfigurerAdapter 的主要功能后面大部分的 xxxConfigurer 都是基于此类来实 现的三、GlobalAuthenticationConfigurerAdapterGlobalAuthenticationConfigurerAdapter 看名字就知道是一个跟全局配置有关的东西它本身实 现了 SecurityConfigurerAdapter 接口但是并未对方法做具体的实现只是将泛型具体化了Order(100) public abstract class GlobalAuthenticationConfigurerAdapter implements SecurityConfigurerAuthenticationManager, AuthenticationManagerBuilder { Override public void init(AuthenticationManagerBuilder auth) throws Exception { } Override public void configure(AuthenticationManagerBuilder auth) throws Exception { } }可以看到SecurityConfigurer 中的泛型现在明确成了 AuthenticationManager 和 AuthenticationManagerBuilder。所以 GlobalAuthenticationConfigurerAdapter 的实现类将来主要 是和配置 AuthenticationManager 有关。当然也包括默认的用户名密码也是由它的实现类来进行配置 的。 我们在 Spring Security 中使用的 AuthenticationManager 其实可以分为两种一种是局部的另一种 是全局的这里主要是全局的配置四、WebSecurityConfigurer还有一个实现类就是 WebSecurityConfigurer这个可能有的小伙伴比较陌生其实他就是我们天 天用的 WebSecurityConfigurerAdapter 的父接口。 所以 WebSecurityConfigurer 的作用就很明确了用户扩展用户自定义的配置五、SecurityConfigurerAdapterSecurityConfigurerAdapter 的实现主要也是三大类UserDetailsAwareConfigurerAbstractHttpConfigurerLdapAuthenticationProviderConfigurer考虑到 LDAP 现在使用很少所以这里我来和大家重点介绍下前两个。1.UserDetailsAwareConfigurer这个配置类看名字大概就知道这是用来配置用户类的。对应的实现类的结构图UserDetailsAwareConfigurer接口public abstract class UserDetailsAwareConfigurerB extends ProviderManagerBuilderB, U extends UserDetailsService extends SecurityConfigurerAdapterAuthenticationManager, B { /** * 返回的是UserDetailsService 接口的实现 * Gets the {link UserDetailsService} or null if it is not available * return the {link UserDetailsService} or null if it is not available */ public abstract U getUserDetailsService(); }通过定义我们可以看到泛型U必须是UserDetailsService接口的实现也就是 getUserDetailsService()方法返回的肯定是UserDetailsService接口的实现还有通过泛型B及继承 SecurityConfigurerAdapter来看会构建一个AuthenticationManager对象AbstractDaoAuthenticationConfigurer接下来我们再看下UserDetailsAwareConfigurer下的一个抽象类 AbstractDaoAuthenticationConfigurer 在类的头部声明了一堆的泛型继承自UserDetailsAwareConfigurerpublic abstract class AbstractDaoAuthenticationConfigurerB extends ProviderManagerBuilderB, C extends AbstractDaoAuthenticationConfigurerB, C, U, U extends UserDetailsService extends UserDetailsAwareConfigurerB, U { // 声明了一个 provider private DaoAuthenticationProvider provider new DaoAuthenticationProvider(); // 声明了一个 userDetailsService 的泛型属性 private final U userDetailsService; /** * Creates a new instance 构造器 传递的对象可以是UserDetailsService或者UserDetailsPasswordService * param userDetailsService */ AbstractDaoAuthenticationConfigurer(U userDetailsService) { this.userDetailsService userDetailsService; this.provider.setUserDetailsService(userDetailsService); if (userDetailsService instanceof UserDetailsPasswordService) { this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService); } } /** * Adds an {link ObjectPostProcessor} for this class. 添加了一个ObjectPostProcessor 后置处理器 * param objectPostProcessor * return the {link AbstractDaoAuthenticationConfigurer} for further customizations */ SuppressWarnings(unchecked) public C withObjectPostProcessor(ObjectPostProcessor? objectPostProcessor) { addObjectPostProcessor(objectPostProcessor); return (C) this; } /** 添加密码编码器 加密 * Allows specifying the {link PasswordEncoder} to use with the * {link DaoAuthenticationProvider}. The default is to use plain text. * param passwordEncoder The {link PasswordEncoder} to use. * return the {link AbstractDaoAuthenticationConfigurer} for further customizations */ SuppressWarnings(unchecked) public C passwordEncoder(PasswordEncoder passwordEncoder) { this.provider.setPasswordEncoder(passwordEncoder); return (C) this; } public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) { this.provider.setUserDetailsPasswordService(passwordManager); return (C) this; } Override public void configure(B builder) throws Exception { // 调用后置处理器 将provider添加到SpringIoC容器中 this.provider postProcess(this.provider); // 将provider添加到builder对象中 builder.authenticationProvider(this.provider); } /** * Gets the {link UserDetailsService} that is used with the * {link DaoAuthenticationProvider} 重写父类的方法 * return the {link UserDetailsService} that is used with the * {link DaoAuthenticationProvider} */ Override public U getUserDetailsService() { return this.userDetailsService; } }UserDetailsServiceConfigurer这个类就比较简单扩展了AbstractDaoAuthenticationConfigurer中的configure方法在 configure 方法执行之前加入了 initUserDetailsService 方法以方便开发展按照自己的方式去初始化 UserDetailsService。不过这里的 initUserDetailsService 方法是空方法public class UserDetailsServiceConfigurerB extends ProviderManagerBuilderB, C extends UserDetailsServiceConfigurerB, C, U, U extends UserDetailsService extends AbstractDaoAuthenticationConfigurerB, C, U { /** * Creates a new instance * param userDetailsService the {link UserDetailsService} that should be used */ public UserDetailsServiceConfigurer(U userDetailsService) { super(userDetailsService); } Override public void configure(B builder) throws Exception { initUserDetailsService(); super.configure(builder); } /** * Allows subclasses to initialize the {link UserDetailsService}. For example, it * might add users, initialize schema, etc. */ protected void initUserDetailsService() throws Exception { } }UserDetailsManagerConfigurerUserDetailsManagerConfigurer 中实现了 UserDetailsServiceConfigurer 中定义的 initUserDetailsService 方法具体的实现逻辑就是将 UserDetailsBuilder 所构建出来的 UserDetails 以及提前准备好的 UserDetails 中的用户存储到 UserDetailsService 中。 该类同时添加了 withUser 方法用来添加用户同时还增加了一个 UserDetailsBuilder 用来构建用 户这些逻辑都比较简单小伙伴们可以自行查看。JdbcUserDetailsManagerConfigurerJdbcUserDetailsManagerConfigurer 在父类的基础上补充了 DataSource 对象同时还提供了相应 的数据库查询方法InMemoryUserDetailsManagerConfigurerInMemoryUserDetailsManagerConfigurer 在父类的基础上重写了构造方法将父类中的 UserDetailsService 实例定义为 InMemoryUserDetailsManagerpublic class InMemoryUserDetailsManagerConfigurerB extends ProviderManagerBuilderB extends UserDetailsManagerConfigurerB, InMemoryUserDetailsManagerConfigurerB { /** * Creates a new instance */ public InMemoryUserDetailsManagerConfigurer() { super(new InMemoryUserDetailsManager(new ArrayList())); } }