Spring Security生效原理
原理
-
SpringSecurity本质是一个排序Filter链(单个类型javax.servlet.Filter的过滤器组成的过滤链),这些链被包装成类型为
org.springframework.security.web.DefaultSecurityFilterChain
过滤链,是org.springframework.security.web.SecurityFilterChain
默认实现类,作用是存放过滤器以及请求匹配规则,本身不能执行过滤逻辑; -
DefaultSecurityFilterChain
通过org.springframework.security.web.FilterChainProxy
代理执行,实现逻辑是通过FilterChainProxy#filterChains
集合属性存放DefaultSecurityFilterChain
,doFilter()方法执行时,从中取出与请求匹配的DefaultSecurityFilterChain,然后遍历执行DefaultSecurityFilterChain中的每个过滤器(通过一个org.springframework.security.web.FilterChainProxy.VirtualFilterChain
的内部类(javax.servlet.FilterChain
的实现类)递归遍历执行),当FilterChainProxy的中的过滤器执行完毕,即DefaultSecurityFilterChain执行完毕,则继续执行Servlet的过滤器;FilterChainProxy被注册到Spring容器中,beanName 为"springSecurityFilterChain" -
FilterChainProxy再次被代理,
org.springframework.web.filter.DelegatingFilterProxy
作为FilterChainProxy的门面被当作Servlet 过滤器添加到Servlet容器中,而DelegatingFilterProxy则通过"springSecurityFilterChain"从Spring容器中获取FilterChainProxy实例,当容器执行到DelegatingFilterProxy过滤器,通过两层代理,最终执行的是过滤链中的过滤器
# FilterChain
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
# Filter
public interface Filter {
void init(FilterConfig var1) throws ServletException;
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
void destroy();
}
#
private static class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest,
FilterChain chain, List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
//如果过滤链中的过滤器执行结束,则继续执行Servlet 自己的过滤链
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
//过滤器索引递增
currentPosition++;
//获取下一个过滤器
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
//过滤器执行过滤方法,并且将过滤链本身作为参数传进去,过滤器执行完本身的逻辑后调用`chain.doFilter(request, response)`即可递归执行
nextFilter.doFilter(request, response, this);
}
}
}
# Filter 实例的 doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//... 其他逻辑省略
chain.doFilter(request, response);
}
流程
须知:实现org.springframework.web.WebApplicationInitializer
的类的onStartup()的会被Servlet容器扫描执行
- org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer#onStartup
AbstractSecurityWebApplicationInitializer
抽象类 实现WebApplicationInitializer
接口
# step 1
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());
insertSpringSecurityFilterChain(servletContext);
afterSpringSecurityFilterChain(servletContext);
}
# step 2
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
// 指定实际的过滤器,该过滤器即为包装过滤器链"springSecurityFilterChain"
String filterName = DEFAULT_FILTER_NAME;
//创建它的代理类
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(
filterName);
String contextAttribute = getWebApplicationContextAttribute();
if (contextAttribute != null) {
springSecurityFilterChain.setContextAttribute(contextAttribute);
}
//将该代理过滤器注册进ServletContext
registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}
# step 3
private final void registerFilter(ServletContext servletContext,
boolean insertBeforeOtherFilters, String filterName, Filter filter) {
//将该代理过滤器添加进ServletContext
Dynamic registration = servletContext.addFilter(filterName, filter);
if (registration == null) {
throw new IllegalStateException(
"Duplicate Filter registration for '" + filterName
+ "'. Check to ensure the Filter is only configured once.");
}
registration.setAsyncSupported(isAsyncSecuritySupported());
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(dispatcherTypes, !insertBeforeOtherFilters,
"/*");
}
- org.springframework.web.filter.DelegatingFilterProxy#doFilter 代理过滤器执行逻辑
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if (delegateToUse == null) {
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if (delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = this.initDelegate(wac);
}
//获取目标过滤器“springSecurityFilterChain”
this.delegate = delegateToUse;
}
}
//调用过滤器执行
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
# step 2
protected void invokeDelegate(Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
- org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain
"springSecurityFilterChain" 实际过滤器的bean注册
# step 1 AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_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);
}
//调用方法创建"springSecurityFilterChain",webSecurity初始化在下面 #step 2
return webSecurity.build();
}
# step 2
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//objectPostProcessor为AutowireBeanFactoryObjectPostProcessor实例,其postProcess方法实现在#step3
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = 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;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
# step 3
public <T> T postProcess(T object) {
if (object == null) {
return null;
}
T result = null;
try {
//初始化属性
result = (T) this.autowireBeanFactory.initializeBean(object,
object.toString());
}
catch (RuntimeException e) {
Class<?> type = object.getClass();
throw new RuntimeException(
"Could not postProcess " + object + " of type " + type, e);
}
this.autowireBeanFactory.autowireBean(object);
if (result instanceof DisposableBean) {
this.disposableBeans.add((DisposableBean) result);
}
if (result instanceof SmartInitializingSingleton) {
this.smartSingletons.add((SmartInitializingSingleton) result);
}
return result;
}
- org.springframework.security.config.annotation.AbstractSecurityBuilder#build
该方法创建实际的
# step 1
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");
}
# step 2 具体创建逻辑
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!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 = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
//DefaultSecurityFilterChain,保存过滤链
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
//通过HttpSecurity(实现AbstractConfiguredSecurityBuilder接口)创建一个"org.springframework.security.web.DefaultSecurityFilterChain"过滤链添加到securityFilterChains中
securityFilterChains.add(securityFilterChainBuilder.build());
}
//实际过滤器链"springSecurityFilterChain",类型为"org.springframework.security.web.FilterChainProxy",
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;
}
- org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild
是一个模板方法
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;
}
}
- org.springframework.security.web.FilterChainProxy#doFilter
# step 1
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
# step 2
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
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);
return;
}
//使用org.springframework.security.web.FilterChainProxy.VirtualFilterChain遍历执行过滤器
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
- org.springframework.security.web.FilterChainProxy.VirtualFilterChain#doFilter
VirtualFilterChain遍历递归执行过滤器
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (currentPosition == size) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " reached end of additional filter chain; proceeding with original chain");
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
originalChain.doFilter(request, response);
}
else {
//初始值为0,每次+1,
currentPosition++;
//从0开始递增获取相应索引的过滤器
Filter nextFilter = additionalFilters.get(currentPosition - 1);
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
+ " at position " + currentPosition + " of " + size
+ " in additional filter chain; firing Filter: '"
+ nextFilter.getClass().getSimpleName() + "'");
}
//执行过滤器,this 递归,每个过滤器处理结束后调用chain.doFilter(request, response);触发递归
nextFilter.doFilter(request, response, this);
}
}
}
过滤器如何添加到springSecurityFilterChain
# step 1 初始化org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#setFilterChainProxySecurityConfigurer中一些参数
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
//标注自动注入数据源
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//创建一个WebSecurity实例,并自动填充属性
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = 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;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
//将配置文件添加到webSecurity
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
# step 1.1 从容器中根据类型获取WebSecurityConfigurer.class实例
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
# step 2 创建springSecurityFilterChain时调用webSecurity.build()
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_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);
}
//调用方法创建"springSecurityFilterChain"
return webSecurity.build();
}
# step 2
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild
是一个模板方法
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
//执行配置文件的init()方法,配置文件在# step 1中初始化
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
buildState = BuildState.BUILDING;
//子类个性化实现创建逻辑,具体实现在上一步
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
# step 3 org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#init
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
- 上述中的配置文件
WebSecurityConfigurer.class
(org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter)实例怎么注入到spring容器中的?
该接口的一个抽象实现为org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
- 自动配置
org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
- 自动配置
@Configuration
@ConditionalOnClass({WebSecurityConfigurerAdapter.class})
@ConditionalOnMissingBean({WebSecurityConfigurerAdapter.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
public class SpringBootWebSecurityConfiguration {
public SpringBootWebSecurityConfiguration() {
}
@Configuration
@Order(2147483642)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
DefaultConfigurerAdapter() {
}
}
}
- 自定义权限管理时使用
@EnableWebSecurity
注解,该注解也是一个@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().mvcMatchers("/user/*", "/login").permitAll()
.anyRequest().authenticated()
/*.mvcMatchers("/**").hasAuthority("")*/
.and().formLogin().successHandler((request, response, authentication) ->
System.out.println("登陆成功!"));
}
}
- 配置文件初始化将
org.springframework.security.config.annotation.web.builders.HttpSecurity
添加到
org.springframework.security.config.annotation.web.builders.WebSecurity#securityFilterChainBuilders
中
# step 1 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init
public void init(final WebSecurity web) throws Exception {
//或HttpSecurity 没有则创建
final HttpSecurity http = getHttp();
//将HttpSecurity作为`org.springframework.security.config.annotation.SecurityBuilder`添加到WebSecurity
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
# step 2 创建HttpSecurity,并往里添加过滤器
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
- 再看
springSecurityFilterChain
的创建过程
# step 1 org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!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 = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
//具体创建过程调用HttpSecurity#build()
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;
}
# step 2 org.springframework.security.config.annotation.web.builders.HttpSecurity#performBuild,可以看出过滤链中有多少过滤器取决于HttpSecurity中定义了多少个过滤器
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
Filter如何添加到HttpSecurity
从上面我们可以直到springSecurityFilterChain
中有多少个过滤器完全取决于org.springframework.security.config.annotation.web.builders.HttpSecurity
中有多少个过滤器,那么HttpSecurity
中的过滤器如何添加进去的?
- HttpSecurity创建springSecurityFilterChain过程
# step 1 上面提到WebSecurity通过调用HttpSecurity#build()方法创建`springSecurityFilterChain`,而所有的实际执行都是调用org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild模板方法
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
//初始化前执行
beforeInit();
//遍历调用所有配置的init()方法,
//即org.springframework.security.config.annotation.SecurityConfigurer#init
init();
buildState = BuildState.CONFIGURING;
//配置前执行
beforeConfigure();
//遍历执行所有的配置的配置方法,
//即org.springframework.security.config.annotation.SecurityConfigurer#configure
configure();
buildState = BuildState.BUILDING;
//实际创建动作,
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
# step 2 org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#init
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
# step 3 org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#configure
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
# step 4 org.springframework.security.config.annotation.web.builders.HttpSecurity#performBuild
# 所以 `springSecurityFilterChain`实质是通过`org.springframework.security.web.FilterChainProxy`包装
# `org.springframework.security.web.DefaultSecurityFilterChain`代理执行
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
- 添加Filter
上面我们知道在创建过程中会执行一个模板方法AbstractConfiguredSecurityBuilder#doBuild
,模板方法在执行实际创建方法前会,会优先执行
AbstractConfiguredSecurityBuilder#init
和AbstractConfiguredSecurityBuilder#configure
,那么HttpSecurity中的Filter应该就是通过这两个方法添加进去的- 在了解Filter如何添加进去之前,先了解配置类
org.springframework.security.config.annotation.SecurityConfigurer
如何添加到HttpSecurity
中的
org.springframework.security.config.annotation.web.builders.HttpSecurity
提供了多种配置方法,让我们可以自由配置各种访问规则或者安全解决方案,每调用一个方法,就会往HttpSecurity中添加一个配置类,如
org.springframework.security.config.annotation.web.builders.HttpSecurity#headers
org.springframework.security.config.annotation.web.builders.HttpSecurity#cors
等
以org.springframework.security.config.annotation.web.builders.HttpSecurity#headers
方法为例
- 在了解Filter如何添加进去之前,先了解配置类
# step 1
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
//添加一个配置类并返回配置类实例,如果Http中已存在该类型的配置类,直接返回
return getOrApply(new HeadersConfigurer<>());
}
# step 2
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}
# step 3
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer)
throws Exception {
configurer.addObjectPostProcessor(objectPostProcessor);
configurer.setBuilder((B) this);
//将配置类添加到httpsecurity
add(configurer);
return configurer;
}
# step 4
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
org.springframework.web.filter.OncePerRequestFilter
(Filter的抽象实现类)添加到org.springframework.security.config.annotation.web.builders.HttpSecurity#filters
同样以org.springframework.security.config.annotation.web.builders.HttpSecurity#headers
方法为例,该方法会往HttpSecurity中添加一个
org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
配置类实例,以配置类型为key,保存到
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#configurers
,同时,HttpSecurity在创建springSecurityFilterChain
实例的模板方法AbstractConfiguredSecurityBuilder#doBuild
中会去遍历每个配置实例,并调用它们的AbstractConfiguredSecurityBuilder#configure
# step 1 org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
public void configure(H http) throws Exception {
//将HeaderWriterFilter添加到HttpSecrity中
HeaderWriterFilter headersFilter = createHeaderWriterFilter();
http.addFilter(headersFilter);
}
- HttpSecurity添加SecurityConfigurer
# step 1 上面我们知道`org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter`实现会被自动注入或者用户自定义使用` @EnableWebSecurity`注入spring容器,并且
#org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init方法会被WebSecurity#build()过程调用
# 即 HttpSecurity在被创建时会添加一些配置类
public void init(final WebSecurity web) throws Exception {
//或HttpSecurity 没有则创建
final HttpSecurity http = getHttp();
//将HttpSecurity作为`org.springframework.security.config.annotation.SecurityBuilder`添加到WebSecurity
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
# step 1.1 org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#getHttp
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
# step 2 我们在自定义配置时可能会重写 `org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)`,这样也会添加一些配置类到HttpSecurity中
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().mvcMatchers("/user/*", "/login").permitAll()
.anyRequest().authenticated()
/*.mvcMatchers("/**").hasAuthority("")*/
.and().formLogin().successHandler((request, response, authentication) ->
System.out.println("登陆成功!"));
}
}
总结
-
WebSecurity 从Spring容器中获取WebSecurityConfigurerAdapter配置类,从配置类中获取HttpSecurity作为
SecurityBuilder<DefaultSecurityFilterChain>
,用于创建DefaultSecurityFilterChain
,并在创建之前执行HttpSecurity中的所有SecurityConfigurer类的init(B builder)方法和configure(B builder)方法 -
HttpSecurity通过提供的方法将SecurityConfigurer添加到AbstractConfiguredSecurityBuilder#configurers中
-
SecurityConfigurer的实现类通过configure(B builder)方法将OncePerRequestFilter添加到HttpSecurity#filters
-
HttpSecurity 通过HttpSecurity#filters创建一个DefaultSecurityFilterChain实例返回给WebSecurity
-
WebSecurity 使用DefaultSecurityFilterChain的实例创建一个FilterChainProxy实例
-
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain方法将FilterChainProxy实例"springSecurityFilterChain"注入到Spring容器中,SpringSecurity的其他自动配置依赖的"springSecurityFilterChain"便从容器中通过约定的beanName获取
总结
WebSecurity用于创建过滤链,由WebSecurityConfiguration创建
HttpSecurity用于配置权限规则,由WebSecurityConfigurerAdapter创建
Security 提供的每个解决方案都有相应的SecurityConfigurer以及Filter(一般通过继承OncePerRequestFilter实现)
SpringSecurity 的原理便是一个FilterChainProxy包装的过滤链,SpringSecurity的自动配置将所有需要Filter添加到该过滤链,使其生效,针对整个过程,扩展思路如下
-
自定义解决方案,自定义过滤器,将其添加到HttpSecurity中
-
修改现有方案的,重写过相应过滤器的逻辑
-
使用Security提供的扩展方法,如一些配置类提供方法支持自定义