【Spring-Security源码分析】Spring Security启动过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shenchaohao12321/article/details/87714141

1、开启Spring Security

将@EnableWebSecurity添加到@Configuration类以在任何WebSecurityConfigurer中定义Spring Security配置,或者更有可能通过扩展WebSecurityConfigurerAdapter基类并覆盖单个方法:

@Configuration
   @EnableWebSecurity
   public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  
   	@Override
   	public void configure(WebSecurity web) throws Exception {
   		web.ignoring()
   		// Spring Security should completely ignore URLs starting with /resources/
   				.antMatchers("/resources/**");
   	}
  
   	@Override
   	protected void configure(HttpSecurity http) throws Exception {
   		http.authorizeRequests().antMatchers("/public/**").permitAll().anyRequest()
   				.hasRole("USER").and()
   				// Possibly more configuration ...
   				.formLogin() // enable form based log in
   				// set permitAll for all URLs associated with Form Login
   				.permitAll();
   	}
  
   	@Override
   	protected void configure(AuthenticationManagerBuilder auth) {
   		auth
   		// enable in memory based authentication with a user named "user" and "admin"
   		.inMemoryAuthentication().withUser("user").password("password").roles("USER")
   				.and().withUser("admin").password("password").roles("USER", "ADMIN");
   	}
  
   	// Possibly more overridden methods ...
   }

2、@EnableWebSecurity

我们自己定义的配置类MyWebSecurityConfiguration加上了@EnableWebSecurity注解,同时继承了WebSecurityConfigurerAdapter。你可能会在想谁的作用大一点,毫无疑问@EnableWebSecurity起到决定性的配置作用,它其实是个组合注解。

@Import({ WebSecurityConfiguration.class,
      SpringWebMvcImportSelector.class,
      OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
   /**
    * Controls debugging support for Spring Security. Default is false.
    * @return if true, enables debug support with Spring Security
    */
   boolean debug() default false;
}

@Import是springboot提供的用于引入外部的配置的注解,可以理解为:@EnableWebSecurity注解激活了@Import注解中包含的配置类。

@EnableGlobalAuthentication注解的源码如下:

@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

注意点同样在@Import之中,它实际上激活了AuthenticationConfiguration这样的一个配置类,用来配置认证相关的核心类。也就是说:@EnableWebSecurity完成的工作便是加载了WebSecurityConfiguration,AuthenticationConfiguration这两个核心配置类,也就此将spring security的职责划分为了配置安全信息,配置认证信息两部分。除了这两个核心的配置类外还有OAuth2ImportSelector和SpringWebMvcImportSelector,OAuth2ImportSelector用于OAuth 2.0客户端支持以后再介绍,下面分别看看SpringWebMvcImportSelector、WebSecurityConfiguration和AuthenticationConfiguration。

3、SpringWebMvcImportSelector

class SpringWebMvcImportSelector implements ImportSelector {
   public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      boolean webmvcPresent = ClassUtils.isPresent(
            "org.springframework.web.servlet.DispatcherServlet",
            getClass().getClassLoader());
      return webmvcPresent
            ? new String[] {
                  "org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration" }
            : new String[] {};
   }
}
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
   private BeanResolver beanResolver;

   @Override
   @SuppressWarnings("deprecation")
   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
      authenticationPrincipalResolver.setBeanResolver(beanResolver);
      argumentResolvers.add(authenticationPrincipalResolver);
      argumentResolvers
            .add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
      argumentResolvers.add(new CsrfTokenArgumentResolver());
   }

   @Bean
   public RequestDataValueProcessor requestDataValueProcessor() {
      //用于为Spring MVC和Spring Security CSRF集成添加RequestDataValueProcessor
      return new CsrfRequestDataValueProcessor();
   }

   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
   }
}

SpringWebMvcImportSelector的作用是为Spring MVC添加了两个HandlerMethodArgumentResolver用来解析@AuthenticationPrincipal参数。

  • org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
  • org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver

对应

  • org.springframework.security.core.annotation.AuthenticationPrincipal
  • org.springframework.security.web.bind.annotation.AuthenticationPrincipal

其中org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver已被标记过期,使用org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver代替它即可。

3.1、@AuthenticationPrincipal的使用

@Controller
public class MyController {
    @MessageMapping("/im")
    public void im(@AuthenticationPrincipal CustomUser customUser) {
        // do something with CustomUser
    }
}

或者,用户可以创建自定义元注释,如下所示:

@Target({ ElementType.PARAMETER })
   @Retention(RetentionPolicy.RUNTIME)
   @AuthenticationPrincipal
   public @interface CurrentUser {
   }

@Controller
   public class MyController {
       @MessageMapping("/im")
       public void im(@CurrentUser CustomUser customUser) {
           // do something with CustomUser
       }
   }

3.2、AuthenticationPrincipalArgumentResolver

允许使用@AuthenticationPrincipal解析Authentication.getPrincipal()。

将使用SecurityContextHolder中的Authentication.getPrincipal()解析CustomUser参数。 如果Authentication或Authentication.getPrincipal()为null,则返回null。 如果类型不匹配,则返回null,除非AuthenticationPrincipal.errorOnInvalidType()为true,否则将抛出ClassCastException。

public boolean supportsParameter(MethodParameter parameter) {
   return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
}

public Object resolveArgument(MethodParameter parameter,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
      WebDataBinderFactory binderFactory) throws Exception {
   Authentication authentication = SecurityContextHolder.getContext()
         .getAuthentication();
   if (authentication == null) {
      return null;
   }
   Object principal = authentication.getPrincipal();

   AuthenticationPrincipal authPrincipal = findMethodAnnotation(
         AuthenticationPrincipal.class, parameter);

   String expressionToParse = authPrincipal.expression();
   if (StringUtils.hasLength(expressionToParse)) {
      StandardEvaluationContext context = new StandardEvaluationContext();
      context.setRootObject(principal);
      context.setVariable("this", principal);
      context.setBeanResolver(beanResolver);

      Expression expression = this.parser.parseExpression(expressionToParse);
      principal = expression.getValue(context);
   }

   if (principal != null
         && !parameter.getParameterType().isAssignableFrom(principal.getClass())) {

      if (authPrincipal.errorOnInvalidType()) {
         throw new ClassCastException(principal + " is not assignable to "
               + parameter.getParameterType());
      }
      else {
         return null;
      }
   }
   return principal;
}

4、AuthenticationConfiguration

用于配置身份认证的AuthenticationManager。

AuthenticationConfiguration会导入一个@Configuration类,向Spring容器注册了一个AutowireBeanFactoryObjectPostProcessor,这个类可以使用Spring容器对其进行初始化和自动装配。

@Configuration
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
   private AtomicBoolean buildingAuthenticationManager = new AtomicBoolean();
   private ApplicationContext applicationContext;
   private AuthenticationManager authenticationManager;
   private boolean authenticationManagerInitialized;
   private List<GlobalAuthenticationConfigurerAdapter> globalAuthConfigurers = Collections
         .emptyList();
   private ObjectPostProcessor<Object> objectPostProcessor;
   //注入ObjectPostProcessorConfiguration配置的AutowireBeanFactoryObjectPostProcessor
   @Autowired
   public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
       this.objectPostProcessor = objectPostProcessor;
   }
   ...
}
@Configuration
public class ObjectPostProcessorConfiguration {
   @Bean
   public ObjectPostProcessor<Object> objectPostProcessor(
         AutowireCapableBeanFactory beanFactory) {
      return new AutowireBeanFactoryObjectPostProcessor(beanFactory);
   }
}

AutowireBeanFactoryObjectPostProcessor允许注册对象以参与AutowireCapableBeanFactory对Aware方法的后处理,InitializingBean.afterPropertiesSet()和DisposableBean.destroy()。

final class AutowireBeanFactoryObjectPostProcessor
      implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton {
   private final Log logger = LogFactory.getLog(getClass());
   private final AutowireCapableBeanFactory autowireBeanFactory;
   private final List<DisposableBean> disposableBeans = new ArrayList<>();
   private final List<SmartInitializingSingleton> smartSingletons = new ArrayList<>();
   public AutowireBeanFactoryObjectPostProcessor(
         AutowireCapableBeanFactory autowireBeanFactory) {
      Assert.notNull(autowireBeanFactory, "autowireBeanFactory cannot be null");
      this.autowireBeanFactory = autowireBeanFactory;
   }
   @SuppressWarnings("unchecked")
   public <T> T postProcess(T object) {
      if (object == null) {
         return null;
      }
      T result = null;
      try {
         result = (T) this.autowireBeanFactory.initializeBean(object,
               object.toString());
      }
      catch (RuntimeException e) {
         Class<?> type = object.getClass();
         throw new RuntimeException(
               "Could not postProcess " + object + " of type " + type, e);
      }
      this.autowireBeanFactory.autowireBean(object);
      if (result instanceof DisposableBean) {
         this.disposableBeans.add((DisposableBean) result);
      }
      if (result instanceof SmartInitializingSingleton) {
         this.smartSingletons.add((SmartInitializingSingleton) result);
      }
      return result;
   }
   @Override
   public void afterSingletonsInstantiated() {
      for (SmartInitializingSingleton singleton : smartSingletons) {
         singleton.afterSingletonsInstantiated();
      }
   }
   public void destroy() throws Exception {
      for (DisposableBean disposable : this.disposableBeans) {
         try {
            disposable.destroy();
         }
         catch (Exception error) {
            this.logger.error(error);
         }
      }
   }
}

4.1、DefaultPasswordEncoderAuthenticationManagerBuilder

配置一个AuthenticationManagerBuilder在getAuthenticationManager()方法中会使用它来构建一个AuthenticationManager,AuthenticationManagerBuilder的源码分析请看《AuthenticationManagerBuilder》

@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(
      ObjectPostProcessor<Object> objectPostProcessor, ApplicationContext context) {
   LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
   AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context, AuthenticationEventPublisher.class);
   //扩展AuthenticationManagerBuilder为其增加一个LazyPasswordEncoder和上面提到的AutowireBeanFactoryObjectPostProcessor
   DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, defaultPasswordEncoder);
   if (authenticationEventPublisher != null) {
      result.authenticationEventPublisher(authenticationEventPublisher);
   }
   return result;
}
//暴露一个方法使用DefaultPasswordEncoderAuthenticationManagerBuilder构建AuthenticationManager
public AuthenticationManager getAuthenticationManager() throws Exception {
   if (this.authenticationManagerInitialized) {
      return this.authenticationManager;
   }
   AuthenticationManagerBuilder authBuilder = authenticationManagerBuilder(
         this.objectPostProcessor, this.applicationContext);
   if (this.buildingAuthenticationManager.getAndSet(true)) {
      return new AuthenticationManagerDelegator(authBuilder);
   }
  
   for (GlobalAuthenticationConfigurerAdapter config : globalAuthConfigurers) {
      authBuilder.apply(config);
   }

   authenticationManager = authBuilder.build();

   if (authenticationManager == null) {
      authenticationManager = getAuthenticationManagerBean();
   }

   this.authenticationManagerInitialized = true;
   return authenticationManager;
}

4.2、GlobalAuthenticationConfigurerAdapter

上面代码会使用GlobalAuthenticationConfigurerAdapter来配置AuthenticationManagerBuilder,只需要实现自己的GlobalAuthenticationConfigurerAdapter通过Spring托管,就会通过setGlobalAuthenticationConfigurers()方法注入进来实现对AuthenticationManagerBuilder的配置。

@Autowired(required = false)
public void setGlobalAuthenticationConfigurers(
      List<GlobalAuthenticationConfigurerAdapter> configurers) throws Exception {
   Collections.sort(configurers, AnnotationAwareOrderComparator.INSTANCE);
   this.globalAuthConfigurers = configurers;
}

 AuthenticationConfiguration中也定义了三个GlobalAuthenticationConfigurerAdapter:

  1. EnableGlobalAuthenticationAutowiredConfigurer:debug级别打印@EnableGlobalAuthentication类信息。
  2. InitializeUserDetailsBeanManagerConfigurer:从Spring中寻找UserDetailsService和PasswordEncoder配置到AuthenticationManagerBuilder。
  3. InitializeAuthenticationProviderBeanManagerConfigurer:从Spring中寻找AuthenticationProvider配置到AuthenticationManagerBuilder。

5、WebSecurityConfiguration

5.1、WebSecurity的创建

使用WebSecurity创建FilterChainProxy,为Spring Security执行基于Web的安全性。 然后它导出必要的bean。 可以通过扩展WebSecurityConfigurerAdapter并将其作为配置或实现WebSecurityConfigurer并将其公开为配置来对WebSecurity进行自定义。 使用EnableWebSecurity时会导入此配置。

这个类中定义了很多@Bean方法,这些方法中很多依赖于变量名是webSecurity的WebSecurity对象,所以我们先从它的创建看起。

关于 WebSecurity的实现原理请参考【Spring-Security源码分析】WebSecurity

//webSecurityConfigurers是下面定义AutowiredWebSecurityConfigurersIgnoreParents的getWebSecurityConfigurers()方法返回值
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
      ObjectPostProcessor<Object> objectPostProcessor,
      @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
      throws Exception {
   //对WebSecurity进行装配
   webSecurity = objectPostProcessor
         .postProcess(new WebSecurity(objectPostProcessor));
   if (debugEnabled != null) {
      webSecurity.debug(debugEnabled);
   }

   Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);

   Integer previousOrder = null;
   Object previousConfig = null;
   for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
      Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
      if (previousOrder != null && previousOrder.equals(order)) {
         throw new IllegalStateException(
               "@Order on WebSecurityConfigurers must be unique. Order of "
                     + order + " was already used on " + previousConfig + ", so it cannot be used on "
                     + config + " too.");
      }
      previousOrder = order;
      previousConfig = config;
   }
   for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
      //使用SecurityConfigurer<Filter, WebSecurity>配置webSecurity
      webSecurity.apply(webSecurityConfigurer);
   }
   this.webSecurityConfigurers = webSecurityConfigurers;
}

@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
      ConfigurableListableBeanFactory beanFactory) {
   return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
//用于从当前ApplicationContext获取所有WebSecurityConfigurer实例但忽略父实例的类。
final class AutowiredWebSecurityConfigurersIgnoreParents {
   private final ConfigurableListableBeanFactory beanFactory;
   public AutowiredWebSecurityConfigurersIgnoreParents(
         ConfigurableListableBeanFactory beanFactory) {
      Assert.notNull(beanFactory, "beanFactory cannot be null");
      this.beanFactory = beanFactory;
   }
   @SuppressWarnings({ "rawtypes", "unchecked" })
   public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
      List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
      Map<String, WebSecurityConfigurer> beansOfType = beanFactory
            .getBeansOfType(WebSecurityConfigurer.class);
      for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
         webSecurityConfigurers.add(entry.getValue());
      }
      return webSecurityConfigurers;
   }
}

5.2、使用WebSecurity创建一个bean name为springSecurityFilterChain的Filter

@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
   boolean hasConfigurers = webSecurityConfigurers != null
         && !webSecurityConfigurers.isEmpty();
   //如果没有提供任何自定义配置,则使用一个默认的WebSecurityConfigurerAdapter配置webSecurity
   if (!hasConfigurers) {
      WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
            .postProcess(new WebSecurityConfigurerAdapter() {
            });
      webSecurity.apply(adapter);
   }
   return webSecurity.build();
}

webSecurity.build()返回一个FilterChainProxy用于用户认证和权限检查,具体细节参考《【Spring-Security源码分析】WebSecurity》

6、如何应用WebSecurity的Filter

现在我们知道了Spring Security真正起作用的就是一个Filter,那么如何将这个过滤器应用到我们的Spring MVC中呢,在非Spring Security的Spring MVC中,我们是通过继承于AbstractAnnotationConfigDispatcherServletInitializer开启我们的Spring MVC功能,具体原理请参考《【Spring源码分析】01-DispatcherServlet注册过程》,而开启带有Spring Security的Spring MVC的功能与之类似,只需要继承AbstractSecurityWebApplicationInitializer即可,因为它的onStartup()方法中会调用insertSpringSecurityFilterChain()方法完成Filter的注册。

public final void onStartup(ServletContext servletContext) throws ServletException {
   beforeSpringSecurityFilterChain(servletContext);
   if (this.configurationClasses != null) {
      AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
      rootAppContext.register(this.configurationClasses);
      servletContext.addListener(new ContextLoaderListener(rootAppContext));
   }
   if (enableHttpSessionEventPublisher()) {
      servletContext.addListener(
            "org.springframework.security.web.session.HttpSessionEventPublisher");
   }
   servletContext.setSessionTrackingModes(getSessionTrackingModes());
   insertSpringSecurityFilterChain(servletContext);
   afterSpringSecurityFilterChain(servletContext);
}
//注册springSecurityFilterChain
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
   String filterName = DEFAULT_FILTER_NAME;
   DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
         filterName);
   String contextAttribute = getWebApplicationContextAttribute();
   if (contextAttribute != null) {
      springSecurityFilterChain.setContextAttribute(contextAttribute);
   }
   //向servlet容器注册DelegatingFilterProxy
   registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}

看到这里只是注册了一个DelegatingFilterProxy,并未直接注册WebSecurityConfiguration定义的springSecurityFilterChain,其实是因为DelegatingFilterProxy是springSecurityFilterChain的委托代理类。在DelegatingFilterProxy的initFilterBean()方法中,会通过Spring容器获取springSecurityFilterChain的bean,然后在doFilter()方法中都是调用这个bean的doFilter()方法。

@Override
protected void initFilterBean() throws ServletException {
   synchronized (this.delegateMonitor) {
      if (this.delegate == null) {
         // If no target bean name specified, use filter name.
         if (this.targetBeanName == null) {
            this.targetBeanName = getFilterName();
         }
         // Fetch Spring root application context and initialize the delegate early,
         // if possible. If the root application context will be started after this
         // filter proxy, we'll have to resort to lazy initialization.
         WebApplicationContext wac = findWebApplicationContext();
         if (wac != null) {
            this.delegate = initDelegate(wac);
         }
      }
   }
}
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
   String targetBeanName = getTargetBeanName();
   Assert.state(targetBeanName != null, "No target bean name set");
   Filter delegate = wac.getBean(targetBeanName, Filter.class);
   if (isTargetFilterLifecycle()) {
      delegate.init(getFilterConfig());
   }
   return delegate;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   // Lazily initialize the delegate if necessary.
   Filter delegateToUse = this.delegate;
   if (delegateToUse == null) {
      synchronized (this.delegateMonitor) {
         delegateToUse = this.delegate;
         if (delegateToUse == null) {
            WebApplicationContext wac = findWebApplicationContext();
            if (wac == null) {
               throw new IllegalStateException("No WebApplicationContext found: " +
                     "no ContextLoaderListener or DispatcherServlet registered?");
            }
            delegateToUse = initDelegate(wac);
         }
         this.delegate = delegateToUse;
      }
   }

   // Let the delegate perform the actual doFilter operation.
   invokeDelegate(delegateToUse, request, response, filterChain);
}

猜你喜欢

转载自blog.csdn.net/shenchaohao12321/article/details/87714141