springSecurityFilterChain源码分析

主要参考点我跳转,写得模糊可移步,原文写得很好。

只要你是一个javaweb项目,权限框架都不可避免的要用到过滤器,shiro和spring security也不避免,所以首要任务就是要知道spring security他的主要过滤器是什么,什么时候创建,什么时候加入过滤器链。

创建

从官方文档我们可知spring security利用名为springSecurityFilterChain的过滤器实现过滤urls,并且实现AbstractSecurityWebApplicationInitializer接口,用以注册过滤器,而实现的方式取决于是否有spring环境,此篇文章只分析具有spring环境的实现,查看我们使用的enablewebsecuroty注解

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.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;
}

引入了两个类,其中springwebmvcimportselector是用以判断是否支持springmvc,并且为springmvc添加security支持,WebSecurityConfiguration看其类注释是利用websecurity创建的filterchainproxy,看代码

/**
	 * Creates the Spring Security Filter Chain
	 * @return
	 * @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();
	}

创建的对象名就是我们需要的springSecurityFilterChain,直接进入build方法,

	public final O build() throws Exception {
		if (this.building.compareAndSet(false, true)) {
			this.object = doBuild();
			return this.object;
		}
		throw new AlreadyBuiltException("This object has already been built");
	}

再次进入dobuild方法

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

再次进入performbuild方法,我们选择websecurity的实现

@Override
	protected Filter performBuild() throws Exception {
		Assert.state(
				!securityFilterChainBuilders.isEmpty(),
				"At least one SecurityBuilder 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 securityFilterChains = new ArrayList<>(
				chainSize);
		for (RequestMatcher ignoredRequest : ignoredRequests) {
			securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
		}
		for (SecurityBuilder 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;
	}

先看返回的类型是一个filter且类型为filterchainproxy,至此springSecurityFilterChain创建

注册

注册的分析比较麻烦,我们并不容易知道他是如何注册的,先是看一遍上面的链接,才发现官方文档都写好了

下一步是注册springSecurityFilterChain战争。这可以在Java配置中使用Spring的WebApplicationInitializer支持在Servlet 3.0+环境中完成。不出所料,Spring Security提供了一个基类AbstractSecurityWebApplicationInitializer,可以确保springSecurityFilterChain为您注册。我们使用的方式AbstractSecurityWebApplicationInitializer取决于我们是否已经使用Spring,或者Spring Security是否是我们应用程序中唯一的Spring组件。

第6.1.2节“没有现有Spring的AbstractSecurityWebApplicationInitializer” - 如果您还没有使用Spring,请使用这些说明
第6.1.3节“使用Spring MVC的AbstractSecurityWebApplicationInitializer” - 如果您已经使用Spring,请使用这些说明

话不多说直接进入AbstractSecurityWebApplicationInitializer,类注释说,为springSecurityFilterChain创建一个代理对象DelegatingFilterProxy,查找DelegatingFilterProxy类发现在方法insertSpringSecurityFilterChain创建,而insertSpringSecurityFilterChain又在onStartup调用,我们现在知道知道onStartup什么时候调用就可以知道什么创建了DelegatingFilterProxy,看其注释

	 * (non-Javadoc)
	 *
	 * @see org.springframework.web.WebApplicationInitializer#onStartup(javax.servlet.
	 * ServletContext)

此方法是实现了WebApplicationInitializer,再次进入WebApplicationInitializer,类注释的大概意思是这个类的配置可以替换以前在web.xml中的配置,比如注册servlet,且让我们参阅SpringServletContainerInitializer,而且此类只有一个方法,其方法的大概作用是可以为ServletContext配置servlet,filter,listener,那到底是怎么替换web.xml中的内容的,我们只能进入SpringServletContainerInitializer一探究竟,SpringServletContainerInitializer的类注释大概意思是ServletContainerInitializer提供了不依靠web.xml启动方式,只要容器(tomcat之类的)满足servlet规范即可,但是需要l路径需要满足一定规则才会被查找到并加载,源码如下

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	/**
	 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
	 * implementations present on the application classpath.
	 * 

Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Spring's {@code WebApplicationInitializer} interface and provide the set of all * such types to the {@code webAppInitializerClasses} parameter of this method. *

If no {@code WebApplicationInitializer} implementations are found on the classpath, * this method is effectively a no-op. An INFO-level log message will be issued notifying * the user that the {@code ServletContainerInitializer} has indeed been invoked but that * no {@code WebApplicationInitializer} implementations were found. *

Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and sorted if the @{@link * org.springframework.core.annotation.Order @Order} annotation is present or * the {@link org.springframework.core.Ordered Ordered} interface has been * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)} * method will be invoked on each instance, delegating the {@code ServletContext} such * that each instance may register and configure servlets such as Spring's * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}, * or any other Servlet API componentry such as filters. * @param webAppInitializerClasses all implementations of * {@link WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized * @see WebApplicationInitializer#onStartup(ServletContext) * @see AnnotationAwareOrderComparator */ @Override public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List initializers = new LinkedList<>(); if (webAppInitializerClasses != null) { for (Class waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }

至此最后一行可知容器在启动时加载了SpringServletContainerInitializer,并调用了onstartup方法,其方法循环调用了WebApplicationInitializer的onstartup,而我们的AbstractSecurityWebApplicationInitializer也正是继承了WebApplicationInitializer得以调用onstartup方法,将springSecurityFilterChain过滤器加入了过滤器链。

结语

至此springsecurity过滤器的创建和注册流程我们已经弄清楚了,由于能力有限并不能分析出源码所用的设计模式,只能推测出他们的一般流程,通过此篇文章我深刻理解到web框架封装的再好也是servlet,filter之类的东西,正如学习jvm不看几遍jvm规范,学习web框架不看servlet规范,只能是雾里看花。

参考链接

https://blog.csdn.net/lqzkcx3/article/details/78507169

https://docs.spring.io/spring-security/site/docs/5.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#tutorial-sample

你可能感兴趣的:(spring,security)