当前位置 : 主页 > 编程语言 > java >

[SpringSecurity5.6.2源码分析一]:spring.factories下有关SpringSecurity的配置类

来源:互联网 收集:自由互联 发布时间:2023-09-06
1、Spring.factories •从下图可以看出 spring-boot-autoconfigure/META-INF/spring.factories中关于SpringSecurity的自动配置类有以下这些 org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\org

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));
   }

}


上一篇:分布式部署指南
下一篇:没有了
网友评论