Spring Security 初始化源码分析

本文基于 spring-boot-2.1.0.RELEASE、spring-security.5.1.1.RELEASE 版本

一、初始化流程入口

从项目的依赖开始

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
 spring-boot-starter-security的依赖
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-config</artifactId>
  <version>5.1.1.RELEASE</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-web</artifactId>
  <version>5.1.1.RELEASE</version>
  <scope>compile</scope>
</dependency>

《spring boot自动装配之@EnableAutoConfiguration详解》可以知道spring
spring boot xxxstarter的类加载入口类都在spring-boot-autoconfigure.jar/META-INF/spring.factories配置文件中
在这里插入图片描述
先看看SecurityAutoConfiguration
在这里插入图片描述
WebSecurityEnablerConfiguration类的注解上可以看到熟悉的@EnableWebSecurity注解了
其中SecurityAutoConfiguration和WebSecurityEnablerConfiguration的加载前提条件
在这里插入图片描述
@ConditionalOnClass中的类已在通过spring-boot-starter-security引入的的spring-security-config项目中。

二、@EnableWebSecurity和springSecurityFilterChain创建

EnableWebSecurity注解Import了WebSecurityConfiguration类,springSecurity的核心的springSecurityFilterChain以及初始的filter都在这个类中被创建

/**
	 * Creates the Spring Security Filter Chain
	 * @return the {@link Filter} that represents the security filter chain
	 * @throws Exception
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME就是XML中定义的springSecurityFilterChain

最终内部会有下面这段代码, 主要关注 init() configure() 和 performBuild() 这三个方法

@Override
protected final O doBuild() throws Exception {
	synchronized (configurers) {
		buildState = BuildState.INITIALIZING;

		beforeInit();
		init();

		buildState = BuildState.CONFIGURING;

		beforeConfigure();
		configure();

		buildState = BuildState.BUILDING;

		O result = performBuild();

		buildState = BuildState.BUILT;

		return result;
	}
}

init() 内部循环遍历 所有的 WebSecurityConfigurer ,它会执行到 WebSecurityConfigurerAdapter

private void init() throws Exception {
	Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

	for (SecurityConfigurer<O, B> configurer : configurers) {
		configurer.init((B) this);
	}

	for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
		configurer.init((B) this);
	}
}
 configurer.init((B) this)

它只要完成两件重要的事情:

  1. 初始化HttpSecurity对象(注意它和WebSecurity不一样 );
  2. 设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中;
public void init(final WebSecurity web) throws Exception {
	final HttpSecurity http = getHttp();
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http
				.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

初始化HttpSecurity对象在getHttp()方法中实现:

protected final HttpSecurity getHttp() throws Exception {
	if (http != null) {
		return http;
	}

	DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
			.postProcess(new DefaultAuthenticationEventPublisher());
	localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);

	AuthenticationManager authenticationManager = authenticationManager();
	authenticationBuilder.parentAuthenticationManager(authenticationManager);
	authenticationBuilder.authenticationEventPublisher(eventPublisher);
	Map<Class<?>, Object> sharedObjects = createSharedObjects();

	http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
			sharedObjects);
	if (!disableDefaults) {
		// @formatter:off
		http
			.csrf().and()
			.addFilter(new WebAsyncManagerIntegrationFilter())
			.exceptionHandling().and()
			.headers().and()
			.sessionManagement().and()
			.securityContext().and()
			.requestCache().and()
			.anonymous().and()
			.servletApi().and()
			.apply(new DefaultLoginPageConfigurer<>()).and()
			.logout();
		// @formatter:on
		ClassLoader classLoader = this.context.getClassLoader();
		List<AbstractHttpConfigurer> defaultHttpConfigurers =
				SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

		for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
			http.apply(configurer);
		}
	}
	configure(http);
	return http;
}

从代码中可以了解,HttpSecurity是直接被new出来的,在创建HttpSecurity之前,首先初始化了AuthenticationManagerBuilder对象,这里有段代码很重要就是: AuthenticationManager authenticationManager = authenticationManager();,它创建AuthenticationManager实例,打开authenticationManager()方法:

默认实现是在 WebSecurityConfigurerAdapter 中
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    this.disableLocalConfigureAuthenticationBldr = true;
}

1、个性化配置入口之configure(AuthenticationManagerBuilder auth)

我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。
在这里插入图片描述
 如下是自己继承WebSecurityConfigurerAdapter 重写 configure(AuthenticationManagerBuilder auth),实现个性化的第一个配置入口

@Configuration
@Slf4j
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {


@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    super.configure(auth);
    log.info("【测试 定制化入口  configure(AuthenticationManagerBuilder auth)  的执行 】");
}
}

构建完HttpSecurity实例后,默认情况下会添加默认的拦截其配置:

    http
        .csrf().and()
        .addFilter(new WebAsyncManagerIntegrationFilter())
        .exceptionHandling().and()
        .headers().and()
        .sessionManagement().and()
        .securityContext().and()
        .requestCache().and()
        .anonymous().and()
        .servletApi().and()
        .apply(new DefaultLoginPageConfigurer<>()).and()
        .logout();

挑一个默认的方法展开看一下比如 会话管理的sessionManagement(),内部就是去创建SessionManagementConfigurer并应用它

public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
	return getOrApply(new SessionManagementConfigurer<>());
}
 getOrApply 最有一句代码 return apply(configurer);

private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
		C configurer) throws Exception {
	C existingConfig = (C) getConfigurer(configurer.getClass());
	if (existingConfig != null) {
		return existingConfig;
	}
	return apply(configurer);
}apply(configurer) 注意这里的 configurer传入的是SessionManagementConfigurer

public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
		throws Exception {
	configurer.addObjectPostProcessor(objectPostProcessor);
	configurer.setBuilder((B) this);
	add(configurer);
	return configurer;
}

最终又调用了 add(configurer); 这不过这里是给 HttpSecurity的 configurers 配置初始的,上面是配置的WebSecurity的configurers, 不要混淆,最终这些configurers会被一个个创建成 对应的过滤器Filter的 详细在后面有说明

private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
	Assert.notNull(configurer, "configurer cannot be null");

	Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
			.getClass();
	synchronized (configurers) {
		if (buildState.isConfigured()) {
			throw new IllegalStateException("Cannot apply " + configurer
					+ " to already built object");
		}
		List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
				.get(clazz) : null;
		if (configs == null) {
			configs = new ArrayList<>(1);
		}
		configs.add(configurer);
		this.configurers.put(clazz, configs);
		if (buildState.isInitializing()) {
			this.configurersAddedInInitializing.add(configurer);
		}
	}
}

如下图:为HttpSecurity添加了很多默认的配置
在这里插入图片描述
  回到 getHttp()方法

最后调用configure(http);,这又是一个可个性化的配置入口,它的默认实现是:WebSecurityConfigurerAdapter提供的

默认的配置是拦截所有的请求需要认证之后才能访问,如果没有认证,会自动生成一个认证表单要求输入用户名和密码。

protected void configure(HttpSecurity http) throws Exception {
	logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

	http
		.authorizeRequests()
			.anyRequest().authenticated()
			.and()
		.formLogin().and()
		.httpBasic();
}

2、个性化配置入口之configure(HttpSecurity http)
 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。

OK,目前为止HttpSecurity已经被初始化,接下去需要设置HttpSecurity对象添加至WebSecurity的securityFilterChainBuilders列表中:

public void init(final WebSecurity web) throws Exception {
	final HttpSecurity http = getHttp();
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http
				.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

当所有的WebSecurityConfigurer的init方法被调用之后,webSecurity.init()工作就结束了

接下去调用了webSecurity.configure(),该方法同样是在AbstractConfiguredSecurityBuilder中实现的:

private void configure() throws Exception {
    Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();

    for (SecurityConfigurer<O, B> configurer : configurers) {
        configurer.configure((B) this);
    }
}

它的主要工作是迭代调用所有WebSecurityConfigurer的configurer方法,参数是WebSeucrity本身,这又是另外一个重要的个性化入口:

3、个性化配置入口之configure(WebSecurity web)
 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity

至此,三个重要的个性化入口都已经被调用,即在实现`WebSecurityConfigurerAdapter经常需要重写的:

1、configure(AuthenticationManagerBuilder auth);
2、configure(WebSecurity web);
3、configure(HttpSecurity http);

 回到webSecurity构建过程,接下去重要的的调用:
java O result = performBuild();  performBuild()

非常重要!!

@Override
protected Filter performBuild() throws Exception {
	Assert.state(
			!securityFilterChainBuilders.isEmpty(),
			() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
					+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
					+ "More advanced users can invoke "
					+ WebSecurity.class.getSimpleName()
					+ ".addSecurityFilterChainBuilder directly");
	int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
	List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
			chainSize);
	for (RequestMatcher ignoredRequest : ignoredRequests) {
		securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
	}
	for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
		securityFilterChains.add(securityFilterChainBuilder.build());
	}
	FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
	if (httpFirewall != null) {
		filterChainProxy.setFirewall(httpFirewall);
	}
	filterChainProxy.afterPropertiesSet();

	Filter result = filterChainProxy;
	if (debugEnabled) {
		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 = new DebugFilter(filterChainProxy);
	}
	postBuildAction.run();
	return result;
}

首先计算出chainSize,也就是ignoredRequests.size() + securityFilterChainBuilders.size();,如果你不配置ignoredRequests,那就是securityFilterChainBuilders.size(),也就是HttpSecurity的个数,其本质上就是你一共配置几个WebSecurityConfigurerAdapter,因为每个WebSecurityConfigurerAdapter对应一个HttpSecurity,而所谓的ignoredRequests就是FilterChainProxy的请求,默认是没有的,如果你需要条跳过某些请求不需要认证或授权,可以如下配置:

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/statics/**");
}

在上面配置中,所有以/statics开头请求都将被FilterChainProxy忽略。

securityFilterChains.add(securityFilterChainBuilder.build()); 这一行就是初始化所有的过滤器,记得上面有段代码如下,将HttpSecurity设置到WebSecurity的 securityFilterChainBuilder中,上面就是调用HttpSecurity.build()方法,初始化所有的 HttpSecurity的过滤器链

public void init(final WebSecurity web) throws Exception {
	final HttpSecurity http = getHttp();
	web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
		FilterSecurityInterceptor securityInterceptor = http
				.getSharedObject(FilterSecurityInterceptor.class);
		web.securityInterceptor(securityInterceptor);
	});
}

依然来到 doBuild()方法,只不过这次是执行的 HttpSecurity的

@Override
protected final O doBuild() throws Exception {
	synchronized (configurers) {
		buildState = BuildState.INITIALIZING;

		beforeInit();
		init();

		buildState = BuildState.CONFIGURING;

		beforeConfigure();
		configure();

		buildState = BuildState.BUILDING;

		O result = performBuild();

		buildState = BuildState.BUILT;

		return result;
	}
}

重点查看 configure()该方法 会调用对应的 过滤器配置的configure()
如 再内部创建 SessionManagementFilter 最后添加到HttpSecurity中,也就是拿 HttpSecurity的configures 一个个创建出对应的过滤器
在这里插入图片描述

@Override
public void configure(H http) {
	SecurityContextRepository securityContextRepository = http
			.getSharedObject(SecurityContextRepository.class);
	SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(
			securityContextRepository, getSessionAuthenticationStrategy(http));
	if (this.sessionAuthenticationErrorUrl != null) {
		sessionManagementFilter.setAuthenticationFailureHandler(
				new SimpleUrlAuthenticationFailureHandler(
						this.sessionAuthenticationErrorUrl));
	}
	InvalidSessionStrategy strategy = getInvalidSessionStrategy();
	if (strategy != null) {
		sessionManagementFilter.setInvalidSessionStrategy(strategy);
	}
	AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();
	if (failureHandler != null) {
		sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
	}
	AuthenticationTrustResolver trustResolver = http
			.getSharedObject(AuthenticationTrustResolver.class);
	if (trustResolver != null) {
		sessionManagementFilter.setTrustResolver(trustResolver);
	}
	sessionManagementFilter = postProcess(sessionManagementFilter);

	http.addFilter(sessionManagementFilter);
	if (isConcurrentSessionControlEnabled()) {
		ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);

		concurrentSessionFilter = postProcess(concurrentSessionFilter);
		http.addFilter(concurrentSessionFilter);
	}
}

doBuild()中的 configure();执行完毕后 的会得到如下HttpSecurity可以看到它内部的filters已经全部创建完毕
在这里插入图片描述
 回到doBuild()方法 该方中有 performBuild() 调用HttpSecurity的 performBuild(),默认实现如下,先对上面所有的过滤器进行排序,使用的是 FilterComparator() 进行排序的,这里不展开了,反正就是会排序成文章开始的那张图上面的顺序

@Override
protected DefaultSecurityFilterChain performBuild() {
	filters.sort(comparator);
	return new DefaultSecurityFilterChain(requestMatcher, filters);
}

最后返回的是SecurityFilterChain的默认实现DefaultSecurityFilterChain
 构建完所有SecurityFilterChain后,创建最为重要的FilterChainProxy实例,
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);

在这里插入图片描述
至此Spring Security 初始化完成,包括springSecurityFilterChain初始化,我们通过继承WebSecurityConfigurerAdapter来代达到个性化配置目的,文中提到了三个重要的个性化入口,并且WebSecurityConfigurerAdapter是可以配置多个的,其对应的接口就是会存在多个SecurityFilterChain实例,但是它们人仍然在同一个FilterChainProxy中,通过RequestMatcher来匹配并传入到对应的SecurityFilterChain中执行请求。

5.个性化入口配置(扩展WebSecurityConfigurerAdapter)

 重要的个性化入口都是哪里调用的 已经在上面初始化 springSecurityFilterChain 源码中讲解了,这里知识总结一下
 1、个性化配置入口之configure(AuthenticationManagerBuilder auth)
 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置AuthenticationManagerBuilder。

2、个性化配置入口之configure(HttpSecurity http)
 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置HttpSecurity。

3、个性化配置入口之configure(WebSecurity web)
 我们可以通过继承WebSecurityConfigurerAdapter并重写该方法来个性化配置WebSecurity。

实现WebSecurityConfigurerAdapter经常需要重写的:
1、configure(AuthenticationManagerBuilder auth);
2、configure(WebSecurity web);
3、configure(HttpSecurity http);

### Spring Security OAuth2 源码解析与授权认证流程 #### 1. 认证请求的初始化Spring Security OAuth2中,`DefaultOAuth2AuthorizationRequestResolver` 类负责解析和构建 `OAuth2AuthorizationRequest` 对象。该对象包含了发起授权请求所需的关键参数,例如 `authorizationRequestUri` 和 `redirectUri`[^2]。 当用户访问受保护的资源时,如果未登录,则会触发重定向到授权服务器的过程。这一过程由 `OAuth2AuthorizationRequestRedirectFilter` 负责实现。通过调用其内部方法 `sendRedirectForAuthorization`,可以将用户的浏览器重定向至指定的授权端点。 --- #### 2. 授权回调的处理机制 一旦用户完成身份验证并同意授权,授权服务器会将控制权返回给应用程序的回调URL。此时,`OAuth2LoginAuthenticationFilter` 将接管后续逻辑,并尝试从回调请求中提取令牌信息。 具体来说,`OAuth2LoginAuthenticationFilter` 使用 `AuthenticationManager` 来管理整个认证过程。其中涉及的核心组件包括: - **AuthenticationProvider**: 提供具体的认证策略。 - **OAuth2LoginAuthenticationProvider**: 实现针对OAuth2协议的具体认证逻辑。 这些组件共同协作,确保能够安全地获取并验证来自授权服务器的身份凭证。 --- #### 3. 安全上下文的更新 认证成功后,Spring Security 会在当前线程的安全上下文中保存经过验证的 `Authentication` 对象。默认情况下,此操作由 `AbstractAuthenticationProcessingFilter#successfulAuthentication` 方法完成,它利用了一个基于 `ThreadLocal<SecurityContext>` 的默策略来存储认证状态[^3]。 这意味着,在同一线程内的任何地方都可以方便地访问已认证的用户信息,从而简化了业务逻辑开发中的权限校验需求。 --- #### 4. 自定义扩展的可能性 尽管Spring Security提供了丰富的内置功能支持标准场景下的OAuth2集成,但在实际项目中可能还需要根据特定需求对其进行定制化调整。例如可以通过配置不同的过滤器链或者替换某些核心服务实例来满足特殊的应用要求[^4]。 此外,对于复杂的微服务架构环境而言,如何有效地管理和同步多个节点之间的Session数据也是一个值得深入探讨的话题。 --- ```java // 示例代码:自定义OAuth2 Login Filter public class CustomOAuth2LoginFilter extends AbstractAuthenticationProcessingFilter { public CustomOAuth2LoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) { super(defaultFilterProcessesUrl); setAuthenticationManager(authenticationManager); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { // 执行额外的操作,比如记录日志或设置Cookie System.out.println("User authenticated successfully: " + authResult.getName()); // 更新Security Context SecurityContextHolder.getContext().setAuthentication(authResult); // 继续执行下一个过滤器 chain.doFilter(request, response); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值