less
@EnableWebSecurity(debug = true)
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(req->req.antMatchers("/api/greeting").authenticated())
.formLogin(login->login.loginPage("/login")
.successHandler(jsonLoginSuccessHandler())
)
.logout(logout->logout.logoutUrl("/perform_logout"));
}
}
我们在springSecuriy中经常去一个类来继承WebSecurityConfigurerAdapter,那为什么在 SecurityConfig
中继承了 WebSecurityConfigurerAdapter
并重写了 configure(HttpSecurity http)
方法后,Spring Security 就会自动调用这个方法?
@EnableWebSecurity
less
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
EnableWebSecurity注解导入了配置类,其中有一个是WebSecurityConfiguration 它是整个 Spring Security 自动装配的"调度中心"。
WebSecurityConfiguration
WebSecurityConfiguration中使用autowired和@value的方式将容器中的SecurityConfigurer<Filter, WebSecurity> 类型注入进来,然后遍历每个webSecurityConfigurer 将其应用在webSecurity
less
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
... 省略不重要的代码
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
从容器中找到名为autowiredWebSecurityConfigurersIgnoreParents的bean 调用他的getWebSecurityConfigurers方法获取所有的SecurityConfigurer集合注入进去,底层通过beanFactory获取容器中所有WebSecurityConfigurer类型的bean
typescript
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
swift
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
webSecurity.apply(webSecurityConfigurer);
调用apply方法 底层调用的是webSecurity的add方法,最后将所有的SecurityConfigurer保存进configurers中,它是个map集合
java
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
swift
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
ini
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
...
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
....
}
}
springSecurityFilterChain
java
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
... 省略不重要的代码
return webSecurity.build();
}
这个方法返回的是整个 Spring Security 的核心过滤器链(Filter
),名字叫 springSecurityFilterChain
,它会被注册进 Spring 容器,并拦截所有请求。
而它的构建依赖于 webSecurity.build()
springSecurityFilterChain
Bean 是整个 Spring Security 的核心过滤器链。
而 .build()
最终会触发所有配置的 configure()
方法
WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
。
在调用 web.build()
时,会进入父类 AbstractConfiguredSecurityBuilder
的逻辑。
ini
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
遍历所有的configure,然后执行他们的init()方法
swift
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
swift
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList<>();
for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
遍历所有的configure,然后执行他们的configure()方法,同时将websecurity作为参数传递进去
scss
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
上面说了build最终会触发所有的configure的init的执行,我们的配置类SecurityConfig继承自WebSecurityConfigurerAdapter,而我们的WebSecurityConfigurerAdapter
scala
public class SecurityConfig extends WebSecurityConfigurerAdapter
csharp
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity>
csharp
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T>
也就是我们的配置类实现了SecurityConfigurer接口,那也就是说服务启动的时候会将SecurityConfig放入前面讨论的configures中 等待调用init进行初始化
WebSecurityConfigurerAdapter中定义的init方法会被调用
ini
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);
});
}
getHttp会被调用,getHttp会创建一个HttpSecurity对象,然后执行
scss
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
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<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
然后执行 configure(http); 下面的configure方法是WebSecurityConfigurerAdapter中默认实现的,但是我们的配置类继承了WebSecurityConfigurerAdapter,所以最终会调用我们自己的configure方法,以上就是为什么会被调用
scss
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}
流程总结

SpEL 表达式语言
在 SpEL 中,"@"符号可以用于引用 Spring 容器中的 bean。通过使用"@beanName"的形式,可以在表达式中获取特定 bean 的实例
-
Bean引用:通过名称引用Spring容器中的bean。
#{@myBean}
:获取名为myBean
的bean实例。
在 SpEL(Spring Expression Language)中,@
用来引用 Spring 容器中的 Bean。
@
在 SpEL 中的含义
@beanName
:表示从 Spring IoC 容器中获取名为beanName
的 Bean 实例。- 它等价于调用
ApplicationContext.getBean("beanName")
。
所以:
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
意思是:
"请从 Spring 容器中找到一个名字叫
autowiredWebSecurityConfigurersIgnoreParents
的 Bean。"
- 当 Spring 解析
ruby
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
1. 找到 `autowiredWebSecurityConfigurersIgnoreParents` 这个 Bean。
2. 调用其 `getWebSecurityConfigurers()` 方法。
3. 将返回的 `List<SecurityConfigurer<...>>` 注入到当前方法的参数中。
⚠️ 注意:WebSAdapter
在 Spring Security 5ecurityConfigurer.7+ 已被标记为过时。