主要参考点我跳转,写得模糊可移步,原文写得很好。
只要你是一个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