书接上回,我们直接引入了spring security的依赖,之后啥也没有干,在访问接口的时候, 就需要认证之后才能访问了 ,咱们没有主动干啥,那肯定有人帮助我们干啥了,这一切都利益出spring boot自动装配机制,下面咱们就看看spring security的自动装配,帮助我们干啥了.
一、Spring Security自动装配
1.1 回顾一下spring boot的自动装配
spring boot会根据类路径中的jar包、类,为jar包里的类自动配置,这样可以极大的减少配置的数量。也就是说spring boot会根据定义在classpath下的类,自动的给你生成一些Bean,并加载到Spring的Context中。
spring boot通过条件注解的配置决定哪些bean可以被自动装配,这些条件定义成具体的xxxAutoConfiguration, 将xxxAutoConfiguration配置到spring.factories文件当中,最好基于spi机制进行加载.但是需要注意的是在spring boot 2.7.0版本及之后的版本,此种方式已经不被推荐使用了,而是加载:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, 如下图所示:
文件内容:
// 部分内容 ....
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
其核心注解@Import(AutoConfigurationImportSelector.class)读取类路径下的文件需要自动装配的类, 一般都是以xxxAutoConfiguration结尾, 再加载的时候,结合条件注解,过滤一下哪些个bean需要加载.以WebMvcAutoConfiguration为例进行说明.
当读取到了WebMvcAutoConfiguration这个类,开始加载所对应的bean.
同样在WebMvcAutoConfiguration类当中,观察一下WebMvcAutoConfigurationAdapter类,如下图所示:
- 重要的小总结
- 读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载各种各样的Bean
- 结合条件注解@Condition,决定加载哪些、不加载哪些Bean
- 一般自动装配的类都是以xxxAutoConfigufation结尾
- 一般默认属性配置类都会在xxxAutoConfiguration类当中引入,以xxxProperties结尾
以Redis自动装配为例,再体会一下.目前我们的工程当中没有引入spring-boot-starer-data-redis依赖,但是在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件当中,有它的自动装配类,如下图所示:
打开瞅瞅
有了以上的知识作为铺垫,我们直接来看spring security自动装配.
1.2 spring security自动装配
1.2.1 @Conditional(DefaultWebSecurityCondition.class)注解
按照我们之前说的,一般都会有xxxAutoConfiguration,我们直接搜索一下瞅瞅. 费劲扒拉的搜了半天,发现直接搜索SpringSecurityAutoConfiguration没有,得搜SecurityAutoConfiguration才能找到.
@AutoConfiguration(before = UserDetailsServiceAutoConfiguration.class)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({
SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
@EnableConfigurationProperties(SecurityProperties.class),表示启用默认装配的各种属性.
@Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class }), 表示引入其它的配置.我们看SpringBootWebSecurityConfiguration.class这个类. 核心代码如下所示:
// Web 安全性的默认配置。它依赖于 Spring Security 的内容协商策略来确定要使用的身份验证类型。
// 如果用户指定了自己的 SecurityFilterChain bean,这将完全回退,
// 用户应该指定他们想要作为自定义安全配置的一部分配置的所有位。
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
return http.build();
}
}
@ConditionalOnDefaultWebSecurity注解用于条件化地配置 Web 安全相关的组件,只有在满足特定条件时才会生效. 其核心代码如下所示:
@Target({
ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(DefaultWebSecurityCondition.class)
public @interface ConditionalOnDefaultWebSecurity {
}
@Conditional(DefaultWebSecurityCondition.class),这个注解告诉 Spring,只有当DefaultWebSecurityCondition中定义的条件满足时,才会应用被注解的配置类、方法或 Bean. 终于到关键代码了:
class DefaultWebSecurityCondition extends AllNestedConditions {
DefaultWebSecurityCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass({
SecurityFilterChain.class, HttpSecurity.class })
static class Classes {
}
@ConditionalOnMissingBean({
SecurityFilterChain.class })
static class Beans {