SpringSecurity5.6.2源码中,spring.factories文件里那些配置类是如何定义的?

2026-04-19 07:352阅读0评论SEO基础
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1115个文字,预计阅读时间需要5分钟。

SpringSecurity5.6.2源码中,spring.factories文件里那些配置类是如何定义的?

1. Spring.factories文件内容如下:从下图中可以看出,spring-boot-autoconfigure/META-INF/spring.factories中关于SpringSecurity的自动配置类包括以下这些:org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\org...

SpringSecurity5.6.2源码中,spring.factories文件里那些配置类是如何定义的?

1、Spring.factories

  • •从下图可以看出 spring-boot-autoconfigure/META-INF/spring.factories中关于SpringSecurity的自动配置类有以下这些

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\

  • •但实际上,去除Reactive和Oauth2的配置类,只剩下下面三个

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\

2、SecurityAutoConfiguration

  • •先从第一个自动配置类开始,主要是有两个作用:
  • •负责导入SpringBootWebSecurityConfiguration、 WebSecurityEnablerConfiguration、SecurityDataConfiguration(后面再说)
  • •在条件允许下往容器注册DefaultAuthenticationEventPublisher

@Configuration @ConditionalOnClass(DefaultAuthenticationEventPublisher.class) @EnableConfigurationProperties(SecurityProperties.class) @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class }) public class SecurityAutoConfiguration { @Bean @ConditionalOnMissingBean(AuthenticationEventPublisher.class) public DefaultAuthenticationEventPublisher authenticationEventPublisher( ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher(publisher); } }

DefaultAuthenticationEventPublisher

  • •主要先看DefaultAuthenticationEventPublisher,作用如下
  • •认证成功后推送认证成功事件
  • •认证失败后推送认证失败事件

public class DefaultAuthenticationEventPublisher implements AuthenticationEventPublisher, ApplicationEventPublisherAware { private final Log logger = LogFactory.getLog(getClass()); private ApplicationEventPublisher applicationEventPublisher; private final HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> exceptionMappings = new HashMap<>(); public DefaultAuthenticationEventPublisher() { this(null); } // 注册认证异常和对应事件的绑定关系 public DefaultAuthenticationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; addMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class); ........ addMapping( "org.springframework.security.authentication.cas.ProxyUntrustedException", AuthenticationFailureProxyUntrustedEvent.class); } // 推送认证成功事件 public void publishAuthenticationSuccess(Authentication authentication) { if (applicationEventPublisher != null) { applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent( authentication)); } } // 推送认证失败事件 public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { Constructor<? extends AbstractAuthenticationEvent> constructor = exceptionMappings .get(exception.getClass().getName()); AbstractAuthenticationEvent event = null; if (constructor != null) { try { event = constructor.newInstance(authentication, exception); } catch (IllegalAccessException | InvocationTargetException | InstantiationException ignored) { } } if (event != null) { if (applicationEventPublisher != null) { applicationEventPublisher.publishEvent(event); } } else { if (logger.isDebugEnabled()) { logger.debug("No event was found for the exception " + exception.getClass().getName()); } } } }

  • •使用场景:在ProviderManager中认证成功或失败推送对应的事件
  • •ProviderManager是认证管理器,在UsernamePasswordAuthenticationFilter等过滤器中调用

3、UserDetailsServiceAutoConfiguration

  • •事实上我们可以在application.yml或者在application.yml中配置登录的用户名和密码然后就可以使用SpringSecurity,其原理也是因这里注入了一个InMemoryUserDetailsManager
  • •当然这种方式也是固定死了用户
  • •从第四行代码也能看出一旦容器中有AuthenticationManager、AuthenticationProvider、UserDetailsService(这三个是自定义认证或者说获取登录的用户的关键类),就不需要基于内存的InMemoryUserDetailsManager

@Configuration(proxyBeanMethods = false) @ConditionalOnClass(AuthenticationManager.class) @ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnMissingBean( // 要求没这三个的原因我估计是因为,如果有了这三个的话,就已经不需要内存的用户的了 value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class }, type = { "org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector" }) public class UserDetailsServiceAutoConfiguration { /** * 密码格式以 {noop} 开头 */ private static final String NOOP_PASSWORD_PREFIX = "{noop}"; /** * 检查密码是否是默认格式的表达式 */ private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\{.+}.*$"); private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class); /** * 为容器中注入一个 {@link InMemoryUserDetailsManager} * @param properties * @param passwordEncoder * @return */ @Bean @ConditionalOnMissingBean( type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository") @Lazy public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) { SecurityProperties.User user = properties.getUser(); List<String> roles = user.getRoles(); return new InMemoryUserDetailsManager( User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())) .roles(StringUtils.toStringArray(roles)).build()); } /** * 返回带有格式的密码 * @param user * @param encoder * @return */ private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) { String password = user.getPassword(); if (user.isPasswordGenerated()) { logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword())); } // 是否带有格式 if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) { return password; } // 加上指定前缀 return NOOP_PASSWORD_PREFIX + password; } }

InMemoryUserDetailsManager

  • •InMemoryUserDetailsManager是UserDetailsManager的非持久实现,主要用于测试和演示目的,其中不需要完整的持久系统
  • •而UserDetailsManager又是UserDetailsService的实现,这个类才是重点
  • •UserDetailsService是加载UserDetails的核心接口,UserDetails是封装用户名,密码等关键信息的

4、SecurityFilterAutoConfiguration

  • •此类作用就是当容器中有一个名称为springSecurityFilterChain的Bean的时候把DelegatingFilterProxyRegistrationBean注册到Spring的容器中去,其实最底层就是将过滤器注册到Servlet容器中,也就是Tomcat容器中,这样在请求进来的时候才能去执行SpringSecurity的过滤器链

@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(SecurityProperties.class) @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class }) @AutoConfigureAfter(SecurityAutoConfiguration.class) public class SecurityFilterAutoConfiguration { private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME; /** * 为了将配置好的 springSecurityFilterChain,注册到Tomcat的容器中 * @param securityProperties * @return */ @Bean @ConditionalOnBean(name = DEFAULT_FILTER_NAME) public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration( SecurityProperties securityProperties) { DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean( DEFAULT_FILTER_NAME); registration.setOrder(securityProperties.getFilter().getOrder()); registration.setDispatcherTypes(getDispatcherTypes(securityProperties)); return registration; } private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) { if (securityProperties.getFilter().getDispatcherTypes() == null) { return null; } return securityProperties.getFilter().getDispatcherTypes().stream() .map((type) -> DispatcherType.valueOf(type.name())) .collect(Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf)); } }


本文共计1115个文字,预计阅读时间需要5分钟。

SpringSecurity5.6.2源码中,spring.factories文件里那些配置类是如何定义的?

1. Spring.factories文件内容如下:从下图中可以看出,spring-boot-autoconfigure/META-INF/spring.factories中关于SpringSecurity的自动配置类包括以下这些:org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\org...

SpringSecurity5.6.2源码中,spring.factories文件里那些配置类是如何定义的?

1、Spring.factories

  • •从下图可以看出 spring-boot-autoconfigure/META-INF/spring.factories中关于SpringSecurity的自动配置类有以下这些

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\

  • •但实际上,去除Reactive和Oauth2的配置类,只剩下下面三个

org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\

2、SecurityAutoConfiguration

  • •先从第一个自动配置类开始,主要是有两个作用:
  • •负责导入SpringBootWebSecurityConfiguration、 WebSecurityEnablerConfiguration、SecurityDataConfiguration(后面再说)
  • •在条件允许下往容器注册DefaultAuthenticationEventPublisher

@Configuration @ConditionalOnClass(DefaultAuthenticationEventPublisher.class) @EnableConfigurationProperties(SecurityProperties.class) @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class }) public class SecurityAutoConfiguration { @Bean @ConditionalOnMissingBean(AuthenticationEventPublisher.class) public DefaultAuthenticationEventPublisher authenticationEventPublisher( ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher(publisher); } }

DefaultAuthenticationEventPublisher

  • •主要先看DefaultAuthenticationEventPublisher,作用如下
  • •认证成功后推送认证成功事件
  • •认证失败后推送认证失败事件

public class DefaultAuthenticationEventPublisher implements AuthenticationEventPublisher, ApplicationEventPublisherAware { private final Log logger = LogFactory.getLog(getClass()); private ApplicationEventPublisher applicationEventPublisher; private final HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> exceptionMappings = new HashMap<>(); public DefaultAuthenticationEventPublisher() { this(null); } // 注册认证异常和对应事件的绑定关系 public DefaultAuthenticationEventPublisher( ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; addMapping(BadCredentialsException.class.getName(), AuthenticationFailureBadCredentialsEvent.class); ........ addMapping( "org.springframework.security.authentication.cas.ProxyUntrustedException", AuthenticationFailureProxyUntrustedEvent.class); } // 推送认证成功事件 public void publishAuthenticationSuccess(Authentication authentication) { if (applicationEventPublisher != null) { applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent( authentication)); } } // 推送认证失败事件 public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { Constructor<? extends AbstractAuthenticationEvent> constructor = exceptionMappings .get(exception.getClass().getName()); AbstractAuthenticationEvent event = null; if (constructor != null) { try { event = constructor.newInstance(authentication, exception); } catch (IllegalAccessException | InvocationTargetException | InstantiationException ignored) { } } if (event != null) { if (applicationEventPublisher != null) { applicationEventPublisher.publishEvent(event); } } else { if (logger.isDebugEnabled()) { logger.debug("No event was found for the exception " + exception.getClass().getName()); } } } }

  • •使用场景:在ProviderManager中认证成功或失败推送对应的事件
  • •ProviderManager是认证管理器,在UsernamePasswordAuthenticationFilter等过滤器中调用

3、UserDetailsServiceAutoConfiguration

  • •事实上我们可以在application.yml或者在application.yml中配置登录的用户名和密码然后就可以使用SpringSecurity,其原理也是因这里注入了一个InMemoryUserDetailsManager
  • •当然这种方式也是固定死了用户
  • •从第四行代码也能看出一旦容器中有AuthenticationManager、AuthenticationProvider、UserDetailsService(这三个是自定义认证或者说获取登录的用户的关键类),就不需要基于内存的InMemoryUserDetailsManager

@Configuration(proxyBeanMethods = false) @ConditionalOnClass(AuthenticationManager.class) @ConditionalOnBean(ObjectPostProcessor.class) @ConditionalOnMissingBean( // 要求没这三个的原因我估计是因为,如果有了这三个的话,就已经不需要内存的用户的了 value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class }, type = { "org.springframework.security.oauth2.jwt.JwtDecoder", "org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector" }) public class UserDetailsServiceAutoConfiguration { /** * 密码格式以 {noop} 开头 */ private static final String NOOP_PASSWORD_PREFIX = "{noop}"; /** * 检查密码是否是默认格式的表达式 */ private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\{.+}.*$"); private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class); /** * 为容器中注入一个 {@link InMemoryUserDetailsManager} * @param properties * @param passwordEncoder * @return */ @Bean @ConditionalOnMissingBean( type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository") @Lazy public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) { SecurityProperties.User user = properties.getUser(); List<String> roles = user.getRoles(); return new InMemoryUserDetailsManager( User.withUsername(user.getName()).password(getOrDeducePassword(user, passwordEncoder.getIfAvailable())) .roles(StringUtils.toStringArray(roles)).build()); } /** * 返回带有格式的密码 * @param user * @param encoder * @return */ private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) { String password = user.getPassword(); if (user.isPasswordGenerated()) { logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword())); } // 是否带有格式 if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) { return password; } // 加上指定前缀 return NOOP_PASSWORD_PREFIX + password; } }

InMemoryUserDetailsManager

  • •InMemoryUserDetailsManager是UserDetailsManager的非持久实现,主要用于测试和演示目的,其中不需要完整的持久系统
  • •而UserDetailsManager又是UserDetailsService的实现,这个类才是重点
  • •UserDetailsService是加载UserDetails的核心接口,UserDetails是封装用户名,密码等关键信息的

4、SecurityFilterAutoConfiguration

  • •此类作用就是当容器中有一个名称为springSecurityFilterChain的Bean的时候把DelegatingFilterProxyRegistrationBean注册到Spring的容器中去,其实最底层就是将过滤器注册到Servlet容器中,也就是Tomcat容器中,这样在请求进来的时候才能去执行SpringSecurity的过滤器链

@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(SecurityProperties.class) @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class }) @AutoConfigureAfter(SecurityAutoConfiguration.class) public class SecurityFilterAutoConfiguration { private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME; /** * 为了将配置好的 springSecurityFilterChain,注册到Tomcat的容器中 * @param securityProperties * @return */ @Bean @ConditionalOnBean(name = DEFAULT_FILTER_NAME) public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration( SecurityProperties securityProperties) { DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean( DEFAULT_FILTER_NAME); registration.setOrder(securityProperties.getFilter().getOrder()); registration.setDispatcherTypes(getDispatcherTypes(securityProperties)); return registration; } private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) { if (securityProperties.getFilter().getDispatcherTypes() == null) { return null; } return securityProperties.getFilter().getDispatcherTypes().stream() .map((type) -> DispatcherType.valueOf(type.name())) .collect(Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf)); } }