本文讨论基于java配置的SpringSecurity源码学习。
Servlet3.0以后推荐使用基于java的方式来配置servlet容器,以替代传统的web.xml.
容器会在类路径下找实现了javax.servlet.ServletContainerInitializer接口的类,如果能发现的话就使用它来配置servlet容器。替代传统的web.xml的功能。
Spring提供了Servlet标准接口的实现类:SpringServletContainerInitializer,这个类牛逼之处在于:又会在”类路径”下查找实现了WebApplicationInitizer接口的所有类,并将真正配置的任务委托给它来实现。具体源码如下,注意看它的参数含义:
/*
* @param webAppInitializerClasses all implementations of
* {@link WebApplicationInitializer} found on the application classpath
* @param servletContext the servlet context to be initialized
*/
@Override
public void onStartup(Set> webAppInitializerClasses, ServletContext servletContext) {
.....
}
这个类是SpringSecurity提供的WebApplicationInitializer的子类。主要做的事情就是:创建DelegatingFilterProxy(“过滤器链代理的委派”),本质上它就是一个过滤器,默认name=springSecurityFilterChain,urlPatterns=/*。意味着,所有的请求都要经过SpringSecurity的处理(包括静态资源哦)
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());
//这里就是具体的创建并向Servlet容器中注册
insertSpringSecurityFilterChain(servletContext);
afterSpringSecurityFilterChain(servletContext);
}
过滤器链代理的委派。前面提到,它本质上是一个Filter,并且它拦截了所有的请求。它其实是SpringSecurity权限管理的入口。它既然是Filter,那么我们就看下它的doFilter()是怎么处理请求的:
@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) {
if (this.delegate == null) {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: " +
"no ContextLoaderListener or DispatcherServlet registered?");
}
//去spring的web容器中找name=springSecurityFilterChain的bean
this.delegate = initDelegate(wac);
}
delegateToUse = this.delegate;
}
}
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
所以从这里可以看出,它确实是一个“代理”,它接受到请求之后,将请求委派给spring web容器中name=springSecurityFilterChain的bean。
基于java配置的SpringSecurity,一般都有这么一个配置类:
@EnableWebSecurity
@ComponentScan("fn.wd.security")
public class SpringSecurityConfiger extends WebSecurityConfigurerAdapter {
...
}
@EnableWebSecurity其实是一个复合注解
@Import({ WebSecurityConfiguration.class, ObjectPostProcessorConfiguration.class,
SpringWebMvcImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {}
啰嗦一下,@Import就类似于xml配置文件中的,后者用于导入其他配置文件(xml),前者用于导入其他配置类(.class)。
所以从这可以看出,这个注解其实导入了三个配置类。
被导入的配置类之一,很简单,就创建了一个AutowireBeanFactoryObjectPostProcessor。
@Bean
public ObjectPostProcessor
它是ObjectPostProcessor的一个实现类,作用是,当手动创建一个对象后(一般手动指的是new xxx()),使用这个后置处理器,为手动创建的类完成依赖注入的功能。
举个例子:
Class A implements BeanFactoryAware{
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory){
this.beanFactory=beanFactory;
}
...
}
Class App{
public static void main(String[] args){
//a是手动创建的,虽然是实现了BeanFactoryAware,但是Spring才不会搭理你
A a=new A();
//假设这里获得了那个后置处理器
autowireBeanFactoryObjectPostProcessor.process(a);
//这样a就能享受spring依赖注入的待遇了
}
}
这个类还是比较重要的。后面会经常用到。关于这个类,知道它是做什么的就好了。
被导入的配置类之一,也比较简单,判断当前是不是SpringMVC环境(看看DispactherServlet在不在类路径下),如果是的话,导入SpringMVC的配置类:WebMvcSecurityConfiguration。
导入的配置类之一。这个算是整个SpringSecurity的配置类了。看它做了些什么?
创建了一个AutowiredWebSecurityConfigurersIgnoreParents对象,并放入容器中:
@Bean
public AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
这个类是干什么用的呢?
答:获取Spring Web容器中的所有类型为WebSecurityConfigurer的bean。源码如下:
public ListFilter, WebSecurity>> getWebSecurityConfigurers() {
ListFilter, WebSecurity>> webSecurityConfigurers = new ArrayListFilter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
获取spring web容器中“过滤器链代理”的所有“配置器”,并将其设置到“过滤器链代理”的“构建器”中。
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor
注意:这里有一个spel的高阶用法:
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
它的意思是,获取容器中的name=autowiredWebSecurityConfigurersIgnoreParents的bean对象,将其getWebSecurityConfigurers()的返回值注入到webSecurityConfigurers属性中。
前面谈到,“过滤器链代理的委派”过滤器将接受到的请求转发给“过滤器链代理”来处理,那这个过滤器链代理是在哪里创建的呢?诺,就在这,创建出来的bean的name正是:springSecurityFilterChain
@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();
}