首页 > 编程语言 >[SpringSecurity5.6.2源码分析一]:spring.factories下有关SpringSecurity的配置类

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

时间:2023-09-03 15:38:19浏览次数:50  
标签:SpringSecurity5.6 spring boot springframework 源码 autoconfigure org security clas

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

}


标签:SpringSecurity5.6,spring,boot,springframework,源码,autoconfigure,org,security,clas
From: https://blog.51cto.com/u_14008019/7340319

相关文章

  • SpringSecurity
    快速入门1.介绍springsecurity是安全框架,准确来说是安全管理框架。相比与另外一个安全框架Shiro,springsecurity提供了更丰富的功能,社区资源也比Shiro丰富springsecurity框架用于Web应用的需要进行认证​和授权​认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪......
  • springboot的管理系统连接虚拟机数据库
    1、在配置文件里面进行更改原来的localhost更改为:虚拟机的IP地址:3306用户名密码更改为:Linux系统MYSQL的帐号密码2、有时因为权限不够,就需要进行权限的授予GRANTALLPRIVILEGESON*.*TO'root'@'%'IDENTIFIEDBY'wingkin45';然后就可能会出现这样的问题:我们可能需......
  • SpringBoot管理系统连接虚拟机MYSQL数据库
    1、使用Navicat软件连接虚拟机ip地址填写虚拟机的:192.168.158.129;密码填写虚拟机的mysql的密码:wingkin45;发现弹出这样一个提示框:2、在虚拟机中查看网络端口信息netstat-ntpl找到3306端口;3、在虚拟机中查看防火墙的状态systemctlstatusfirewalld没有3306端口,则就是......
  • javeee spring cglib动态代理
    cglib动态代理依赖<dependency><groupId>cglib</groupId><artifactId>cglib-nodep</artifactId><version>3.2.4</version></dependency>代理类packagecom.test.cglibProxy;importnet.sf.cglib.proxy.En......
  • javaee spring依赖注入之spel方式
    spring依赖注入之spel方式<dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>4.3.18.RELEASE</version></dependency>packagecom.test.pojo;im......
  • idea启动项目报错Error:(5, 52) java: 程序包org.springframework.beans.factory.anno
    idea启动项目报错Error:(5,52)java:程序包org.springframework.beans.factory.annotation不存在IDEA启动项目报错ERROR:(5,52)JAVA:程序包ORG.SPRINGFRAMEWORK.BEANS.FACTORY.ANNOTATION不存在去IDEA下查找maven选项:如果还不行,则继续选择下面的runner,勾选deleteIDEbuild......
  • springboot - 整合redis
    1.引入pom依赖<!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--fastjson序列化器--><dependency><grou......
  • javaee spring 测试aop 切面
    切面类packagecom.test.advice;importorg.aspectj.lang.ProceedingJoinPoint;//增强类publicclassMyAdvice{//将这个增强方法切入到service层的add方法前publicvoidbefore(){System.out.println("添加用户之前");}}目标类packag......
  • javaee spring 静态代理
    静态代理packagecom.test.staticProxy;publicinterfaceIUsersService{publicvoidinsert();}packagecom.test.staticProxy;//目标类publicclassUsersServiceimplementsIUsersService{@Overridepublicvoidinsert(){System.out.print......
  • javaee spring jdbc 查询方法
    javaeespringjdbc查询方法packagecom.test.dao.impl;importcom.test.dao.IUsersDao;importcom.test.pojo.Users;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.jdbc......