默认Springboot Security安全配置自动加载在SecurityAutoConfiguration和UserDetailsServiceAutoConfiguration。
具体请看spring-boot-autoconfigure-2.0.7.RELEASE.jar下的/META-INF/spring.factories文件
SecurityAutoConfiguration类:导入SpringBootWebSecurityConfiguration拦截响应的请求;UserDetailsServiceAutoConfiguration类:配置身份验证。
@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(
ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 没有AuthenticationManager、AuthenticationProvider和UserDetailsService,加载该bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
....省略
}
如果要完全关闭默认Web应用程序安全配置,您可以添加bean继承 WebSecurityConfigurerAdapter,这样Springboot将不会初始化默认的WebSecurityConfigurerAdapter (这样做不会禁用UserDetailsService配置或Actuator的安全性)。
SpringBootWebSecurityConfiguration 的代码如下:
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
// 没有WebSecurityConfigurerAdapter类或子类加载默认配置
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
关闭UserDetailsService的配置,您可以添加类继承 UserDetailsService,AuthenticationProvider或AuthenticationManager,来覆盖Springboot默认实现。
UserDetailsServiceAutoConfiguration 代码如下:
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
// 没有AuthenticationManager、AuthenticationProvider和UserDetailsService,加载该bean配置系想你
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean(type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(
SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser();
List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager(User.withUsername(user.getName())
.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
.roles(StringUtils.toStringArray(roles)).build());
}
}
经过上面分析我们知道怎么关闭Springboot默认实现,接着我们看SecurityAutoConfiguration 类中导入的WebSecurityEnablerConfiguration配置信息
@Configuration
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
}
@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;
}
查看源码我们发现WebSecurityConfiguration配置了Security关键信息
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
@Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor;
......省略
@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();
}
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
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;
}
......省略
}
开始时会将所有实现SecurityConfigurer接口的类注入到webSecurityConfigurers参数中
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
// 创建webSecurity对象 ,应为是new来的对象,没有走Spring的生命周期,可以利用objectPostProcessor让new来的对象走Spring的生命周期
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
// 主要检验了配置实例的order顺序
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;
}
该方法中 主要执行如下
1、创建webSecurity对象
2、主要检验了配置实例的order顺序(order唯一 否则会报错)
3、将所有的配置实例存放进入到webSecurity对象中,其中配置实例中含有我们自定义业务的权限控制配置信息
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
// 这个方法会判断我们上一个方法中有没有获取到webSecurityConfigurers,没有的话这边会创建一个WebSecurityConfigurerAdapter实例,并追加到websecurity中
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
// 调用websecurity的build方法
return webSecurity.build();
}
该方法中 主要执行如下
1、这个方法会判断我们上一个方法中有没有获取到webSecurityConfigurers,没有的话这边会创建一个WebSecurityConfigurerAdapter实例,并追加到websecurity中
2、调用websecurity的build方法
SecurityBuilder继承关系:
AbstractSecurityBuilder类build()方法
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
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");
}
......省略
}
调用子类AbstractConfiguredSecurityBuilder的doBuild()方法
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
@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;
}
}
protected void beforeInit() throws Exception {
}
@SuppressWarnings("unchecked")
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);
}
}
protected void beforeConfigure() throws Exception {
}
@SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
......省略
}
build过程主要分三步,init->configure->peformBuild ,具体流程图
1、调用getHttp()方法获取一个http实例,getHttp()方法
Ⅰ、添加认证的事件的发布者
Ⅱ、配置认证管理器,会调用我们的继承的WebSecurityConfigurerAdapter中重写的
configure(AuthenticationManagerBuilder auth)方法,配置认证相关信息
Ⅲ、追加各种SecurityConfigurer的具体实现类到httpSecurity中,如exceptionHandling()方法会追加一个
ExceptionHandlingConfigurer,sessionManagement()方法会追加一个SessionManagementConfigurer,
securityContext()方法会追加一个SecurityContextConfigurer对象,
这些SecurityConfigurer的具体实现类最终会为我们配置各种具体的filter
Ⅳ、最终调用我们的继承的WebSecurityConfigurerAdapter中重写的configure(HttpSecurity http),
将我们业务相关的权限配置规则信息进行初始化操作。默认的configure(HttpSecurity http)方法继续向httpSecurity类中追加
SecurityConfigurer的具体实现类,如authorizeRequests()方法追加一个ExpressionUrlAuthorizationConfigurer,
formLogin()方法追加一个FormLoginConfigurer。 其中ExpressionUrlAuthorizationConfigurer这个实现类比较重要,
因为他会给我们创建一个非常重要的对象FilterSecurityInterceptor对象,FormLoginConfigurer对象比较简单,
但是也会为我们提供一个在安全认证过程中经常用到会用的一个Filter:UsernamePasswordAuthenticationFilter。
2、通过web.addSecurityFilterChainBuilder方法把获取到的HttpSecurity实例赋值给WebSecurity的securityFilterChainBuilders属性
从init可以看出一个WebSecurity可以生成多个HttpSecurity,而在HttpSecurity又会创建多个拦截器,最终封装到FilterChainProxy对象中
以上三个方法就是WebSecurityConfigurerAdapter类中init方法的主要逻辑,
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
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);
}
});
}
@SuppressWarnings({ "rawtypes", "unchecked" })
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);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
// 追加各种SecurityConfigurer的具体实现类到httpSecurity中
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();
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
//最终调用我们的继承的WebSecurityConfigurerAdapter中重写的configure(),将我们业务相关的权限配置规则信息进行初始化操作
configure(http);
return http;
}
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}
}
configure方法最终也调用到会我们继承的WebSecurityConfigurerAdapter中重写的configure(WebSecurity web)方法,默认实现中这个是一个空方法,具体应用中也经常重写这个方法来实现特定需求。
具体的实现逻辑在WebSecurity类中
遍历securityFilterChainBuilders属性中的SecurityBuilder对象,并调用他的build方法。
这个securityFilterChainBuilders属性我们前面也有提到过(具体请看2.2.1.1 init方法),就是在WebSecurityConfigurerAdapter类的init方法中获取http后赋值给了WebSecurity。因此这个地方就是调用httpSecurity的build方法。
httpSecurity的build方式向其中追加一个个过滤器
@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));
}
// 遍历securityFilterChainBuilders属性中的SecurityBuilder对象,并调用他的build方法。
for (SecurityBuilder<? extends SecurityFilterChain> 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;
}
在peformBuild方法,会在webSecurity类调用httpSecurity的build(具体请看2.2.1.3 peformBuild方法)
httpSecurity也继承于SecurityBuilder,因此也会走build过程,init->configure->peformBuil
在init的时候我们可以看到,会执行这些配置信息,这是在2.2.1.1 init方法中注入的,
在configure方法中,会调用所有的configures,调用该类中的configure方法初始化filter类
@SuppressWarnings("unchecked")
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
以CsfConfigurer为例
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
@SuppressWarnings("unchecked")
@Override
public void configure(H http) throws Exception {
// 创建Filter并配置
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
if (requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
}
AccessDeniedHandler accessDeniedHandler = createAccessDeniedHandler(http);
if (accessDeniedHandler != null) {
filter.setAccessDeniedHandler(accessDeniedHandler);
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer
.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http
.getConfigurer(SessionManagementConfigurer.class);
if (sessionConfigurer != null) {
sessionConfigurer.addSessionAuthenticationStrategy(
new CsrfAuthenticationStrategy(this.csrfTokenRepository));
}
filter = postProcess(filter);
http.addFilter(filter);
}
}
最后再看下HttpSecurity类执行build的最后一步 performBuild,这个方法就是在HttpSecurity中实现的
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
可以看到,这个类只是把我们追加到HttpSecurity中的security进行了排序,用的排序类是FilterComparator,从而保证我们的filter按照正确的顺序执行。接着将filters构建成filterChian返回。在前面WebSecurity的performBuild方法中,这个返回值会被包装成FilterChainProxy,并作为WebSecurity的build方法的放回值。从而以springSecurityFilterChain这个名称注册到springContext中(在WebSecurityConfiguration中做的)
ExpressionUrlAuthorizationConfigurer的继承关系
ExpressionUrlAuthorizationConfigurer->AbstractInterceptUrlConfigurer->AbstractHttpConfigurer->SecurityConfigurerAdapter->SecurityConfigurer
对应的init方法在SecurityConfigurerAdapter类中,是个空实现,什么也没有做,configure方法在SecurityConfigurerAdapter类中也有一个空实现,在AbstractInterceptUrlConfigurer类中进行了重写