SecurityConfigurer 在 Spring Security 中是一个非常重要的角色。Spring Security 过滤器链中的每一个过滤器,都是通过 xxxConfigurer 来进行配置的,而这些 xxxConfigurer 实际上都是 SecurityConfigurer 的实现。
public interface SecurityConfigurer> { //一个初始化方法 void init(B builder) throws Exception; //一个配置方法。这里只是规范了方法的定义,具体的实现则在不同的实现类中 void configure(B builder) throws Exception; }
SecurityConfigurer 有三种类型实现类:SecurityConfigurerAdapter,GlobalAuthenticationConfigurerAdapter和WebSecurityConfigurer
本文主要介绍的是WebSecurityConfigurer
1、WebSecurityConfigurerpublic interface WebSecurityConfigurer> extends SecurityConfigurer { }
这里边的泛型很关键,这关乎到 WebSecurityConfigurer 的目的是啥!
SecurityBuilder 中的泛型 Filter,表示 SecurityBuilder 最终的目的是为了构建一个 Filter 对象出来。
SecurityConfigurer 中两个泛型,第一个表示的含义也是 SecurityBuilder 最终构建的对象。
同时这里还定义了新的泛型 T,T 需要继承自 SecurityBuilder,根据 WebSecurityConfigurerAdapter 中的定义,我们可以知道,T 就是 WebSecurity,我们也大概能猜出 WebSecurity 就是 SecurityBuilder 的子类。
所以 WebSecurityConfigurer 的目的我们可以理解为就是为了配置 WebSecurity。
WebSecurityConfigurer的子类图如下
@Order(100) public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer2.1 密码解析器的配置{ private ApplicationContext context; //提供@Autowired(required = false)方法注入 private ContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy(); //通过@Autowired方法注入 private ObjectPostProcessor
@Order(100) public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer{ static class LazyPasswordEncoder implements PasswordEncoder { private ApplicationContext applicationContext; private PasswordEncoder passwordEncoder; LazyPasswordEncoder(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public String encode(CharSequence rawPassword) { return getPasswordEncoder().encode(rawPassword); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { return getPasswordEncoder().matches(rawPassword, encodedPassword); } @Override public boolean upgradeEncoding(String encodedPassword) { return getPasswordEncoder().upgradeEncoding(encodedPassword); } private PasswordEncoder getPasswordEncoder() { if (this.passwordEncoder != null) { return this.passwordEncoder; } //从容器中创建获取PasswordEncoder对象 PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class); if (passwordEncoder == null) { passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); } this.passwordEncoder = passwordEncoder; return passwordEncoder; } private T getBeanOrNull(Class type) { try { return this.applicationContext.getBean(type); } catch(NoSuchBeanDefinitionException notFound) { return null; } } @Override public String toString() { return getPasswordEncoder().toString(); } } }
从上面代码可以看出,PasswordEncoder的创建发生在第一次解析密码时,他先从容器中获取PasswordEncoder对象,如果没有就使用默认的。
2.3 HttpSecurity的初始化过程如果我要配置我们想要的密码解析器PasswordEncoder,直接在配置类将它加入容器中进行,不需要做额外的操作
@Order(100) public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer{ //HttpSecurity的初始化发生在这里 public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); } protected final HttpSecurity getHttp() throws Exception { if (http != null) {return http;} DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor .postProcess(new DefaultAuthenticationEventPublisher()); localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); //创建AuthenticationManager AuthenticationManager authenticationManager = authenticationManager(); authenticationBuilder.parentAuthenticationManager(authenticationManager); authenticationBuilder.authenticationEventPublisher(eventPublisher); Map , Object> sharedObjects = createSharedObjects(); http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects); if (!disableDefaults) { http .csrf().and() .addFilter(new WebAsyncManagerIntegrationFilter()) .exceptionHandling().and() .headers().and() .sessionManagement().and() .securityContext().and() .requestCache().and() .anonymous().and() .servletApi().and() .apply(new DefaultLoginPageConfigurer<>()).and() .logout(); // @formatter:on ClassLoader classLoader = this.context.getClassLoader(); List defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader); for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) { http.apply(configurer); } } configure(http); return http; } protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); } }
这里,我们要提下他的这几个方法
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthenticationManagerDelegator(authenticationBuilder, context);
}
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
public UserDetailsService userDetailsServiceBean() throws Exception {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
protected UserDetailsService userDetailsService() {
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
}
2.3 配置方法
public void configure(WebSecurity web) throws Exception {}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
protected void configure(HttpSecurity http) throws Exception {
}
3、WebSecurityConfigurerAdapter的注入位置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration(proxyBeanMethods = false)
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}



