核心思想
spring对请求的处理过程如下:
而security所有认证逻辑作为特殊的一个Filter加入到spring处理servelet的过滤链中,即FilterChainProxy;
而这个FilterChainProxy内部又有多个过滤器链FilterChain,每个链有matcher,用于匹配请求,请求来时选择最先匹配的一条过滤器链做权限认证,每条过滤器链又由多个多滤器Filter依序连接而成。如下图:
编程范式
configuator配置类装配到builder类中,builder类借助confuguator类构造filter或者filterChain
SpringSecurity有两个重要的builder:
- WebSecurity:构造FilterChainProxy(由多条过滤器链SecurityFilterChain组成的代理)
- HttpSecurity:构造过滤器链SecurityFilterChain(DefaultSecurityFilterChain)
Spring如何加载FilterChainProxy
首先生成FilterChainProxy实例,将FilterChainProxy实例再封装到DelegatingFilterProxy(java web的标准过滤器),作为一个web的Filter再注册到spring上下文。
生成FilterChainProxy实例
- 通过SpringBootApplication注解加载EnableAutoConfiguration注解
@SpringBootApplication
public class AuthCodeServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthCodeServerApplication.class, args);
}
}
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class,
attribute = "exclude"
)
......
- 自动加载SecurityAutoConfiguation配置类
在spring.factories文件中有
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\
......
- 加载SpringBootWebSecurityConfiguration配置类
@Import({SpringBootWebSecurityConfiguration.class, AuthenticationManagerConfiguration.class, BootGlobalAuthenticationConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
......
- 加载EnableWebSecurity注解
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({EnableWebSecurity.class, AuthenticationEntryPoint.class})
@ConditionalOnMissingBean({WebSecurityConfiguration.class})
@ConditionalOnWebApplication
@EnableWebSecurity
public class SpringBootWebSecurityConfiguration {
......
- 加载WebSecurityConfiguation配置类
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
- 通过WebSecurityConfiguation配置类的springSecurityFilterChain方法 产生FilterChainProxy 实例(实例名为springSecurityFilterChain)
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
......
@Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
return (Filter)this.webSecurity.build();
}
至于this.webSecurity.build()内部怎么实现的,后面再讲。
将FilterChainProxy实例注册到spring
将FilterChainProxy再封装到DelegatingFilterProxy,在注入到spring上下文。有两种方式,如下:
- 通过SecurityFilterAutoConfiguration配置类
@Bean
@ConditionalOnBean(
name = {"springSecurityFilterChain"}
)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
registration.setOrder(securityProperties.getFilterOrder());
registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
return registration;
}
- 通过继承实现AbstractSecurityWebApplicationInitializer抽象类
在onStartUp方法里调用insertSpringSecurityFilterChain方法注入
public final void onStartup(ServletContext servletContext) throws ServletException {
this.beforeSpringSecurityFilterChain(servletContext);
if (this.configurationClasses != null) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(this.configurationClasses);
servletContext.addListener(new ContextLoaderListener(rootAppContext));
}
if (this.enableHttpSessionEventPublisher()) {
servletContext.addListener("org.springframework.security.web.session.HttpSessionEventPublisher");
}
servletContext.setSessionTrackingModes(this.getSessionTrackingModes());
this.insertSpringSecurityFilterChain(servletContext);
this.afterSpringSecurityFilterChain(servletContext);
}
......
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
String filterName = "springSecurityFilterChain";
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);
String contextAttribute = this.getWebApplicationContextAttribute();
if (contextAttribute != null) {
springSecurityFilterChain.setContextAttribute(contextAttribute);
}
this.registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}
详解FilterChainProxy实例如何产生
上面讲到了通过调用this.webSecurity.build()方法产生FilterChainProxy实例,现在仔细分析具体怎么实现的。
- 实例化并配置WebSecurity
在WebSecurityConfiguration配置类,通过setFilterChainProxySecurityConfigurer方法实例化WebSecurity,并搜索实现了WebSecurityConfigurer(WebSecurityConfigurerAdapter)的所有配置类,加载到WebSecurity中
@Autowired(
required = false
)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
Collections.sort(webSecurityConfigurers, WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
Iterator var5;
SecurityConfigurer config;
for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
config = (SecurityConfigurer)var5.next();
Integer order = WebSecurityConfiguration.AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
}
previousOrder = order;
}
var5 = webSecurityConfigurers.iterator();
while(var5.hasNext()) {
config = (SecurityConfigurer)var5.next();
this.webSecurity.apply(config);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
- 调用WebSucurity 的build方法构造FilterChainProxy实例
见上面第5步
最终会调到doBuild方法。
protected final O doBuild() throws Exception {
synchronized(this.configurers) {
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
this.beforeInit();
this.init();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
this.beforeConfigure();
this.configure();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
O result = this.performBuild();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
return result;
}
}
由上可见,主要是init、configure、performBuild三个方法
2.1. init(WebSecurity)
会遍历所有之前加载好的配置类configuator(adaptor),调用其init。
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
SecurityConfigurer configurer;
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
var2 = this.configurersAddedInInitializing.iterator();
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
}
其中配置类的init方法,主要是构造了HttpSecurity,放入到securityFilterChainBuilders;并在postBuild之后设置inteceptor到websecurity。可见WebSecurityConfigurerAdapter类的init方法实现:
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = this.getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
2.2. configure(WebSecurity)
会遍历所有之前加载好的配置类configuator(adaptor),调用其configure
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
while(var2.hasNext()) {
SecurityConfigurer<O, B> configurer = (SecurityConfigurer)var2.next();
configurer.configure(this);
}
}
配置类的configure方法,主要是配置上一步构造好的HttpSecurity实例,将其相关的configuator配置类装配到HttpSecurity实例。可见WebSecurityConfigurerAdapter类的configure方法实现:
protected void configure(HttpSecurity http) throws Exception {
this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic();
}
我们在定义自己的过滤器链的时候,可以继承WebSecurityConfigurerAdapter重写configure方法,比如:
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers() // 指定当前`SecurityFilterChain`实例匹配哪些请求
.anyRequest().and()
.authorizeRequests() // 拦截请求,创建FilterSecurityInterceptor
.anyRequest().authenticated() // 在创建过滤器的基础上的一些自定义配置
.and() // 用and来表示配置过滤器结束,以便进行下一个过滤器的创建和配置
.formLogin().and() // 设置表单登录,创建UsernamePasswordAuthenticationFilter
.httpBasic(); // basic验证,创建BasicAuthenticationFilter
}
2.3. performBuild(WebSecurity)
遍历securityFilterChainBuilders(其实就是HttpSecurity)列表调用其build方法,生成SecurityFilterChain实例,最后利用多个SecurityFilterChain实例组成List,再封装到FilterChainProxy。
protected Filter performBuild() throws Exception {
Assert.state(!this.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 = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);
Iterator var3 = this.ignoredRequests.iterator();
while(var3.hasNext()) {
RequestMatcher ignoredRequest = (RequestMatcher)var3.next();
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]));
}
var3 = this.securityFilterChainBuilders.iterator();
while(var3.hasNext()) {
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var3.next();
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.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);
}
this.postBuildAction.run();
return (Filter)result;
}
securityFilterChainBuilders(其实就是HttpSecurity)的build方法,内部最后也是调用自己的init、configure、performBuild。
2.3.1. init(HttpSecurity)
会遍历所有之前加载好的配置类configuator,调用其init
配置类的init一般是配置HttpSecurity。以HttpBasicConfigurer配置类为例:
public void init(B http) throws Exception {
this.registerDefaults(http);
}
private void registerDefaults(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = (ContentNegotiationStrategy)http.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher restMatcher = new MediaTypeRequestMatcher((ContentNegotiationStrategy)contentNegotiationStrategy, new MediaType[]{MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML});
restMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notHtmlMatcher = new NegatedRequestMatcher(new MediaTypeRequestMatcher((ContentNegotiationStrategy)contentNegotiationStrategy, new MediaType[]{MediaType.TEXT_HTML}));
RequestMatcher restNotHtmlMatcher = new AndRequestMatcher(Arrays.asList(notHtmlMatcher, restMatcher));
RequestMatcher preferredMatcher = new OrRequestMatcher(Arrays.asList(X_REQUESTED_WITH, restNotHtmlMatcher));
this.registerDefaultEntryPoint(http, preferredMatcher);
this.registerDefaultLogoutSuccessHandler(http, preferredMatcher);
}
2.3.2. configure(HttpSecurity)
会遍历所有之前加载好的配置类configuator,调用其configure
配置类的configure一般构造Filter,添加到HttpSecurity的Filter列表中,作为过滤器链的其中一个。以HttpBasicConfigurer配置类为例:
public void configure(B http) throws Exception {
AuthenticationManager authenticationManager = (AuthenticationManager)http.getSharedObject(AuthenticationManager.class);
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager, this.authenticationEntryPoint);
if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
RememberMeServices rememberMeServices = (RememberMeServices)http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
}
basicAuthenticationFilter = (BasicAuthenticationFilter)this.postProcess(basicAuthenticationFilter);
http.addFilter(basicAuthenticationFilter);
}
2.3.3. performBuild(HttpSecurity)
将List<Filter>以及matcher封装成SecurityFilterChain
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(this.filters, this.comparator);
return new DefaultSecurityFilterChain(this.requestMatcher, this.filters);
}
请求来时,FilterChainProxy如何起作用
请求到达的时候,FilterChainProxy的dofilter()方法内部,会遍历所有的SecurityFilterChain,匹配url,第一个匹配到之后则调用该SecurityFilterChain中的List<filter>依次做认证或鉴权,执行最后调用外层传入的filterchain,将控制权交给上层过滤链,即spring 的过滤链。
核心逻辑如下:
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);//选择匹配的过滤器链
if (filters != null && filters.size() != 0) {
FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);//将请求交给该过滤器链,逐个通过filter进行验证
} else {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);//security逻辑已执行完,交给上层
}
}
//取第一个匹配的过滤器链
private List<Filter> getFilters(HttpServletRequest request) {
Iterator var2 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if (!var2.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var2.next();
} while(!chain.matches(request));
return chain.getFilters();
}