0


Spring Security 6.1.x 系列(4)—— 基于过滤器链的源码分析

一、自动配置

在 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);}));}}
标签: Spring Security

本文转载自: https://blog.csdn.net/ctwy291314/article/details/134155035
版权归原作者 gmHappy 所有, 如有侵权,请联系我们删除。

“Spring Security 6.1.x 系列(4)—— 基于过滤器链的源码分析”的评论:

还没有评论