一、自动配置
在 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中我们只引入
spring-boot-starter-security
依赖,就可以实现登录认证,这些都得益于
Spring Boot
的自动配置。
在
spring-boot-autoconfigure
模块中集成了对
Spring Security
的自动配置:
默认的配置是由
SecurityAutoConfiguration
、
UserDetailsServiceAutoConfiguration
、
SecurityFilterAutoConfiguration
这三个自动配置类实现的。
二、SecurityAutoConfiguration
SecurityAutoConfiguration
主要是导入
SpringBootWebSecurityConfiguration
配置:
@AutoConfiguration(
before ={UserDetailsServiceAutoConfiguration.class})@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})@EnableConfigurationProperties({SecurityProperties.class})@Import({SpringBootWebSecurityConfiguration.class,SecurityDataConfiguration.class})publicclassSecurityAutoConfiguration{publicSecurityAutoConfiguration(){}@Bean@ConditionalOnMissingBean({AuthenticationEventPublisher.class})publicDefaultAuthenticationEventPublisherauthenticationEventPublisher(ApplicationEventPublisher publisher){returnnewDefaultAuthenticationEventPublisher(publisher);}}
2.1 SpringBootWebSecurityConfiguration
在
SpringBootWebSecurityConfiguration
配置类中,默认添加了
@EnableWebSecurity
注解,启用了
Spring Security
应用安全配置,并完成
defaultSecurityFilterChain
构建:
@Configuration(
proxyBeanMethods =false)@ConditionalOnWebApplication(
type =Type.SERVLET)classSpringBootWebSecurityConfiguration{SpringBootWebSecurityConfiguration(){}@Configuration(
proxyBeanMethods =false)@ConditionalOnMissingBean(
name ={"springSecurityFilterChain"})@ConditionalOnClass({EnableWebSecurity.class})@EnableWebSecuritystaticclassWebSecurityEnablerConfiguration{WebSecurityEnablerConfiguration(){}}@Configuration(
proxyBeanMethods =false)@ConditionalOnDefaultWebSecuritystaticclassSecurityFilterChainConfiguration{SecurityFilterChainConfiguration(){}@Bean@Order(2147483642)SecurityFilterChaindefaultSecurityFilterChain(HttpSecurity http)throwsException{
http.authorizeHttpRequests((requests)->{// 配置所有的Http请求必须认证((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();});// 开启表单登录认证
http.formLogin(Customizer.withDefaults());// 开启basic认证
http.httpBasic(Customizer.withDefaults());return(SecurityFilterChain)http.build();}}}
2.1.1 DefaultSecurityFilterChain构建
Spring Security
提供了默认实现类
DefaultSecurityFilterChain
,上文源码中
defaultSecurityFilterChain
通过
HttpSecurity#build
方法构建,跟踪进入
HttpSecurity#performBuild
源码:
从上面看得出
HttpSecurity
就是一个构建类,它的使命就是构建出一个
SecurityFilterChain
,查看
DefaultSecurityFilterChain
默认匹配所有请求,并默认存在15存在过滤器:
2.2 @EnableWebSecurity
@EnableWebSecurity
中会导入多个配置类:
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})@Documented@Import({WebSecurityConfiguration.class,SpringWebMvcImportSelector.class,OAuth2ImportSelector.class,HttpSecurityConfiguration.class})@EnableGlobalAuthenticationpublic@interfaceEnableWebSecurity{booleandebug()defaultfalse;}
下文重点介绍下
WebSecurityConfiguration
配置类。
2.2.1 springSecurityFilterChain构建
springSecurityFilterChain
在
WebSecurityConfiguration
中通过
WebSecurity#build
方法构建:
跟踪进入
WebSecurity#performBuild
源码:
@OverrideprotectedFilterperformBuild()throwsException{Assert.state(!this.securityFilterChainBuilders.isEmpty(),()->"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "+"Typically this is done by exposing a SecurityFilterChain bean. "+"More advanced users can invoke "+WebSecurity.class.getSimpleName()+".addSecurityFilterChainBuilder directly");int chainSize =this.ignoredRequests.size()+this.securityFilterChainBuilders.size();List<SecurityFilterChain> securityFilterChains =newArrayList<>(chainSize);List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries =newArrayList<>();for(RequestMatcher ignoredRequest :this.ignoredRequests){WebSecurity.this.logger.warn("You are asking Spring Security to ignore "+ ignoredRequest
+". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");SecurityFilterChain securityFilterChain =newDefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));}for(SecurityBuilder<?extendsSecurityFilterChain> securityFilterChainBuilder :this.securityFilterChainBuilders){SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));}if(this.privilegeEvaluator ==null){this.privilegeEvaluator =newRequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);}FilterChainProxy filterChainProxy =newFilterChainProxy(securityFilterChains);if(this.httpFirewall !=null){
filterChainProxy.setFirewall(this.httpFirewall);}if(this.requestRejectedHandler !=null){
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);}elseif(!this.observationRegistry.isNoop()){CompositeRequestRejectedHandler requestRejectedHandler =newCompositeRequestRejectedHandler(newObservationMarkingRequestRejectedHandler(this.observationRegistry),newHttpStatusRequestRejectedHandler());
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);}
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
filterChainProxy.afterPropertiesSet();Filter result = filterChainProxy;if(this.debugEnabled){this.logger.warn("\n\n"+"********************************************************************\n"+"********** Security debugging is enabled. *************\n"+"********** This may include sensitive information. *************\n"+"********** Do not use in a production system! *************\n"+"********************************************************************\n\n");
result =newDebugFilter(filterChainProxy);}this.postBuildAction.run();return result;}
springSecurityFilterChain
会被
FilterChainProxy
代理,注册为
Bean
,并存放了所有的
SecurityFilterChain
:
FilterChainProxy
是一个
GenericFilterBean
(即是
Servlet Filter
又是
Spring Bean
),它管理了所有注入
Spring IoC
容器的
SecurityFilterChain
。
三、UserDetailsServiceAutoConfiguration
UserDetailsServiceAutoConfiguration
只是通过加载
Yml
配置文件生成一个默认用户,以便于开发测试:
@AutoConfiguration@ConditionalOnClass({AuthenticationManager.class})@ConditionalOnBean({ObjectPostProcessor.class})@ConditionalOnMissingBean(
value ={AuthenticationManager.class,AuthenticationProvider.class,UserDetailsService.class,AuthenticationManagerResolver.class},
type ={"org.springframework.security.oauth2.jwt.JwtDecoder","org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector","org.springframework.security.oauth2.client.registration.ClientRegistrationRepository","org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository"})publicclassUserDetailsServiceAutoConfiguration{privatestaticfinalStringNOOP_PASSWORD_PREFIX="{noop}";privatestaticfinalPatternPASSWORD_ALGORITHM_PATTERN=Pattern.compile("^\\{.+}.*$");privatestaticfinalLog logger =LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);publicUserDetailsServiceAutoConfiguration(){}@BeanpublicInMemoryUserDetailsManagerinMemoryUserDetailsManager(SecurityProperties properties,ObjectProvider<PasswordEncoder> passwordEncoder){SecurityProperties.User user = properties.getUser();List<String> roles = user.getRoles();returnnewInMemoryUserDetailsManager(newUserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user,(PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});}privateStringgetOrDeducePassword(SecurityProperties.User user,PasswordEncoder encoder){String password = user.getPassword();if(user.isPasswordGenerated()){
logger.warn(String.format("%n%nUsing generated security password: %s%n%nThis generated password is for development use only. Your security configuration must be updated before running your application in production.%n", user.getPassword()));}return encoder ==null&&!PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()?"{noop}"+ password : password;}}
看到这里我们就能明白解 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中通过在配置文件中定义用户名和密码能生效的原因。
四、SecurityFilterAutoConfiguration
在
SecurityFilterAutoConfiguration
自动配置类中,
springSecurityFilterChain
之前被声明构建过,为
FilterChainProxy
类型,代理了
Spring Security
中所有的
SecurityFilterChain
。
因为
Servlet
容器和
Spring IoC
容器之间的
Filter
生命周期并不匹配。
为了让
Spring IoC
容器管理
Filter
的生命周期,
FilterChainProxy
(也就是
springSecurityFilterChain
)便交由
Spring Web
下的
DelegatingFilterProxy
来代理。
而且
FilterChainProxy
不会在任何过滤器
Bean
上调用标准
Servlet
过滤器生命周期方法,
FilterChainProxy
的生命周期方法会委托给
DelegatingFilterProxy
来执行。
而
DelegatingFilterProxy
作为
Servlet
容器和
Spring IoC
容器的生命周期之间桥接的存在。
@AutoConfiguration(
after ={SecurityAutoConfiguration.class})@ConditionalOnWebApplication(
type =Type.SERVLET)@EnableConfigurationProperties({SecurityProperties.class})@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class,SessionCreationPolicy.class})publicclassSecurityFilterAutoConfiguration{privatestaticfinalStringDEFAULT_FILTER_NAME="springSecurityFilterChain";publicSecurityFilterAutoConfiguration(){}@Bean@ConditionalOnBean(
name ={"springSecurityFilterChain"})publicDelegatingFilterProxyRegistrationBeansecurityFilterChainRegistration(SecurityProperties securityProperties){DelegatingFilterProxyRegistrationBean registration =newDelegatingFilterProxyRegistrationBean("springSecurityFilterChain",newServletRegistrationBean[0]);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));return registration;}privateEnumSet<DispatcherType>getDispatcherTypes(SecurityProperties securityProperties){return securityProperties.getFilter().getDispatcherTypes()==null?null:(EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type)->{returnDispatcherType.valueOf(type.name());}).collect(Collectors.toCollection(()->{returnEnumSet.noneOf(DispatcherType.class);}));}}
版权归原作者 gmHappy 所有, 如有侵权,请联系我们删除。