@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().accessDeniedPage("/403").and()
.formLogin().loginPage("/login").defaultSuccessUrl("/home").failureUrl("/login?fail_type=0")
.and().authorizeRequests().antMatchers("/login", "/js/**", "/css/**", "/image/**", "/403").permitAll()
.anyRequest().authenticated().and()
.sessionManagement().invalidSessionUrl("/login").maximumSessions(1500).and().and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll().and()
.rememberMe();
}
}
首先看下 Spring Security 的配置代码片断,从上面的代码中可以看到,有配置一个VerifyCodeFilter
在 UsernamePasswordAuthenticationFilter
之前,那这段代码是怎么起作用的呢?
-
Spring 的初始化,会经过
WebApplicationInitializer
(SPI), 搜索代码,可以看到 在Spring Security 的包中有定义一个AbstractSecurityWebApplicationInitializer
,其中有实现方法 onStartuppublic 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); }
其中的方法 insertSpringSecurityFilterChain
private void insertSpringSecurityFilterChain(ServletContext servletContext) { String filterName = DEFAULT_FILTER_NAME; //springSecurityFilterChain DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy( filterName); String contextAttribute = getWebApplicationContextAttribute(); if (contextAttribute != null) { springSecurityFilterChain.setContextAttribute(contextAttribute); } registerFilter(servletContext, true, filterName, springSecurityFilterChain); }
从方法名称可以看出,这里是为了插入 Spring Security 的Filter 链,而这个代理类是
DelegatingFilterProxy
,这个类的构造方法public DelegatingFilterProxy(String targetBeanName) { this(targetBeanName, null); }
而最终 也是去找到对应的Filter 进行处理,这个Filter 是通过 beanName 为springSecurityFilterChain 去 Spring 容器中获取
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; }
从上面的分析知道,处理Spring Security 相关的代码,最终是以 BeanName 为springSecurityFilterChain 的对象进行处理的,so,现在我们来看下,这个bean 是在哪里定义的?
我们来看下
@EnableWebSecurity
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { java.lang.annotation.ElementType.TYPE }) @Documented @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; }
这里会导入
WebSecurityConfiguration
,在这个类中,有声明这样的Bean@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(); }
这个我们就找到了,BeanName 为springSecurityFilterChain 的Bean的注册点了
同时我们发现,这里的Filter 是通过 WebSecurity 这个对象来创建的
- 获取
WebSecurityConfigurerAdapter
的实现类 - 把
WebSecurityConfigurerAdapter
添加到 WebSecurity 的Configure 列表中 - 调用 WebSecurity.build 方法,会调用r接口
SecurityConfigurer
的方法,init,configure,SecurityBuilder
build方法
@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; } }
-
WebSecurityConfigurerAdapter
是实现SecurityConfigurer
接口的来看init 方法
public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() { public void run() { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); } }); }
从这里可以看出,WebSecurity 也是把创建Filter的动作,转接给HttpSecurity 这个动作了,这就是为什么在开始的时候,配置HttpSecurity 这个对象,就可以把Filter 直接传递给 ServletContext
- 获取