说明
使用spring-boot 我们引入security的包 就可以自动实现简单的登录,是怎么做到的呢?
知道spring-security源码,我们的可以通过打断点方式,找到各个核心源码处,知道各个配置原理,和扩展点 完成业务定制化逻辑
security自动化配置
1.在spring-boot-autoconfigure的spring.factories引入了security的自动化配置。我们主要看最核心的org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
spriing自动化配置原理可以参考《Spring Boot-Starter(九)》
2.SecurityAutoConfiguration实现
Import导入原理可以参考《spring源码阅读(五)-Spring Import注解使用》《Spring源码阅读(六)-ConfigurationClassPostProcessor》
@Configuration( proxyBeanMethods = false ) @ConditionalOnClass({DefaultAuthenticationEventPublisher.class})//class path有此类加载 @EnableConfigurationProperties({SecurityProperties.class}) //Import导入 我们主要看WebSecurityEnablerConfiguration @Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class}) public class SecurityAutoConfiguration { public SecurityAutoConfiguration() { } @Bean @ConditionalOnMissingBean({AuthenticationEventPublisher.class})//容器没有这个bean触发加载 public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) { return new DefaultAuthenticationEventPublisher(publisher); } }
3.我们继续看EnableWebSecurity
@Configuration( proxyBeanMethods = false//不需要代理 ) @ConditionalOnBean({WebSecurityConfigurerAdapter.class})//容器中有WebSecurityConfigurerAdapter对象触发自动加载 @ConditionalOnMissingBean(//容器中不能出现springSecurityFilterChain的实例 name = {"springSecurityFilterChain"} ) @ConditionalOnWebApplication( type = ConditionalOnWebApplication.Type.SERVLET ) @EnableWebSecurity//组合注解 public class WebSecurityEnablerConfiguration { public WebSecurityEnablerConfiguration() { } }
4.
这里又用到了Import导入 我们主要看WebSecurityConfiguration
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.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; }
5.WebSecurityConfiguration首先会初始化一个webSecurity管理我们定义的WebSecurityConfigurerAdapter子类
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#setFilterChainProxySecurityConfigurer
/** *autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers() * 这个类里面就是根据beanFactory获取WebSecurityConfigurer的实现 也就是我们的配置的WebSecurityConfigurerAdapter子类 */ @Autowired(required = false) public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { //这里是通过new创建WebSecurity 同时通过objectPostProcessor 从容器中找如果有的话就依赖注入 //我们可以阅读里面成员变量原理 通过容器注入对应对象完成初始化复制 this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); if (this.debugEnabled != null) { this.webSecurity.debug(this.debugEnabled); } //排序 webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null; Object previousConfig = null; for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) { 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; previousConfig = config; } //循环遍历设置到 add 到webSecurity成员变量configurers webSecurityConfigures为我们自定义继承的WebSecurityConfigureAdapter配置类 for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { this.webSecurity.apply(webSecurityConfigurer); } this.webSecurityConfigurers = webSecurityConfigurers; }
6.
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain"; 最后通过org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration 注入到Servlet
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty(); boolean hasFilterChain = !this.securityFilterChains.isEmpty(); Assert.state(!(hasConfigurers && hasFilterChain), "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one."); //我们没有配置 这里应该是配置一个默认的 if (!hasConfigurers && !hasFilterChain) { WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); this.webSecurity.apply(adapter); } /** * securityFilterChains 是通过容器获取 通过@Autowired set方法注入 * 这里也是一个扩展点 我们可以增加手动增加securityFilterChain */ for (SecurityFilterChain securityFilterChain : this.securityFilterChains) { this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain); for (Filter filter : securityFilterChain.getFilters()) { //如果这个filter是FilterSecurityInterceptor 则加入到securityInterceptor if (filter instanceof FilterSecurityInterceptor) { this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter); break; } } } /** * webSecurityCustomizers也是从容器获取 * 也是一个扩展点。我们可以自定义 在build前对webSecurity做一些定制化操作 * @通过@Autowired set方法注入 */ for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) { customizer.customize(this.webSecurity); } /* *<1>执行build */ return this.webSecurity.build(); }
<1>
模板模式
org.springframework.security.config.annotation.AbstractSecurityBuilder#build
@Override public final O build() throws Exception { if (this.building.compareAndSet(false, true)) { //<2>模板模式 this.object = doBuild(); return this.object; } throw new AlreadyBuiltException("This object has already been built"); }
<2>
所有配置都继承这个类
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild
/** * @return * @throws Exception */ @Override protected final O doBuild() throws Exception { synchronized (this.configurers) { this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING; //空实现 beforeInit(); //<3>初始化config init(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING; beforeConfigure(); //<4> configure(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
//模板方法 抽象的子类必须实现 真正的build方法 O result = performBuild(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT; return result; } }
针对不同的build会调用不同的performBuild方法
如webSecurity则是调用<10>
如HttpSecurity则调用<13>
针对DefaultPasswordEncoderAuthenticationManagerBuilder 则调用<16>
<3>
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#init
@SuppressWarnings("unchecked") private void init() throws Exception { //获得configures调用configures的init 注意不同的配置类 就是调用不同配置的init方法 Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.init((B) this); } for (SecurityConfigurer<O, B> configurer : this.configurersAddedInInitializing) { configurer.init((B) this); } }
针对不同配置 Configurers不一样,如果是WebSecurity则getConfigures是 WebSecurityConfigurerAdapter 所以调用的WebSecurityConfigurerAdapter的init方法<6>
<4>
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#configure
@SuppressWarnings("unchecked") private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { //不同的配置类就是调用不同的 configure方法初始化 configurer.configure((B) this); } }
针对不同配置 Configurers不一样
1.如果是WebSecurity则getConfigures是 WebSecurityConfigurerAdapter 所以调用的WebSecurityConfigurerAdapter的configure方法<5>
2.如果是HttpSecurity则是<11>处配置的各种config如 如果有需要可以研究各个config如何初始化的比如我们参考HeaderConfigure的实现<12>
3.针对DefaultPasswordEncoderAuthenticationManagerBuilder 的confgure请看<15>
<15>
<5>
一般我们都会重写
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**", "/css/**", "/images/**"); }
<6>
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init
@Override public void init(WebSecurity web) throws Exception { //<7>初始化HttpSecurity 本事也是一个build HttpSecurity http = getHttp(); //将HttpSecurity add 到WebSecurity 后续会使用securityFilterChainBuilders web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); }
<7>
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#getHttp
@SuppressWarnings({ "rawtypes", "unchecked" }) protected final HttpSecurity getHttp() throws Exception { if (this.http != null) { return this.http; } //从容器获取AuthenticationEventPublisher AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher(); //设置到localConfigureAuthenticationBldr build this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); //<8>通过localConfigureAuthenticationBldr build初始化AuthenticationManager 这里是org.springframework.security.authentication.ProviderManager AuthenticationManager authenticationManager = authenticationManager(); //给authenticationBuilder 设置authenticationManager this.authenticationBuilder.parentAuthenticationManager(authenticationManager); Map<Class<?>, Object> sharedObjects = createSharedObjects(); //初始化HttpSecurity this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects); if (!this.disableDefaults) { //<11>进行默认配置 applyDefaultConfiguration(this.http); ClassLoader classLoader = this.context.getClassLoader(); List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader .loadFactories(AbstractHttpConfigurer.class, classLoader); for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) { this.http.apply(configurer); } } /** * <9>传入我们的http build 让我们可以做定制化配置 * @Override * protected void configure(HttpSecurity http) throws Exception{ * .... * } */ configure(this.http); return this.http; }
<8>
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#authenticationManager
protected AuthenticationManager authenticationManager() throws Exception { //避免重复初始化 if (!this.authenticationManagerInitialized) { /** * 这里就是调用自定义继承WebSecurityConfigurerAdapter重写的configure(AuthenticationManagerBuilder auth) * 传入build 让我们可以自定义一些参数配置 比如配置用户信息是基于应用 还是内存 * auth.inMemoryAuthentication() * .withUser("liqiang").password("liqiang").roles("admin") * .and() * .withUser("admin").password("admin").roles("admin"); */ configure(this.localConfigureAuthenticationBldr); // if (this.disableLocalConfigureAuthenticationBldr) { //这里是一个扩展点我们可以直接 authenticationConfiguration是根据spring容器初始化的 根据authenticationConfiguration而不是通过build this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager(); } else { //正常是走得这个build方法 build authenticationManager 这里会调用<1> 为何到1请看下面说明 //默认 localConfigureAuthenticationBldr是DefaultPasswordEncoderAuthenticationManagerBuilder //初始化处 详看:<14> this.authenticationManager = this.localConfigureAuthenticationBldr.build(); } this.authenticationManagerInitialized = true; } return this.authenticationManager; }
configure(this.localConfigureAuthenticationBldr);
这里需要强调的一点是调用我们继承的WebSecurityConfigurerAdapter 我们可以定义用户管理器
如基于应用内存
内部创建inMemoryAuthentication方法 创建InMemoryUserDetailsManagerConfigurer 到build confgures
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.inMemoryAuthentication() .withUser("liqiang").password("liqiang").roles("admin") .and() .withUser("admin").password("admin").roles("admin"); }
自定义userDetail
内部创建DaoAuthenticationConfigurer 到build confgures
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.userDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return null; } }); }
基于封装的jdbc查询jdbcAuthentication方法 创建JdbcUserDetailsManagerConfigurer 到build confgures
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { /** * inMemoryAuthentication 开启在内存中定义用户 * 多个用户通过and隔开 */ auth.jdbcAuthentication().dataSource(null).usersByUsernameQuery(""); }
他们本质都是根据auth.创建不同的config对象 设置到build的configures
<9>
/** * 对于不需要授权的静态文件放行 * @param web * @throws Exception */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**", "/css/**", "/images/**"); }
<10>
org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild
securityFilterChains为什么是列表
因为不同的url可以有不同的处理逻辑
@Override protected Filter performBuild() throws Exception { Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. " + "Typically this is done by exposing a SecurityFilterChain bean " + "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. " + "More advanced users can invoke " + WebSecurity.class.getSimpleName() + ".addSecurityFilterChainBuilder directly"); /** * 得我们设置的忽略检查为他们添加一个 这里会添加3个chains 根据匹配做不通过处理 * public void configure(WebSecurity web) throws Exception { * web.ignoring().antMatchers("/js/**", "/css/**", "/images/**"); * } */ int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } //securityFilterChainBuilders为HttpSecurity<6>处初始化 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { //执行build<1> 最终会构建成<13> securityFilterChains.add(securityFilterChainBuilder.build()); } //通过FilterChainProxy 代理管理 它实现了ServletFilter 通过FilterChainProxy为Servlet入口 进入security的自己的filter FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (this.httpFirewall != null) { filterChainProxy.setFirewall(this.httpFirewall); } if (this.requestRejectedHandler != null) { filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (this.debugEnabled) { result = new DebugFilter(filterChainProxy); } this.postBuildAction.run(); //返回filter 我们请求都会到filterChainProxy 通过他调用security的filter实现securityfilter 注入逻辑 return result; }
<11>
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#applyDefaultConfiguration
private void applyDefaultConfiguration(HttpSecurity http) throws Exception { //http本质也是build 这里都是配置默认的config configure add CsrfConfigurer http.csrf(); //默认增加一个WebAsyncManagerIntegrationFilter http.addFilter(new WebAsyncManagerIntegrationFilter()); //configures add ExceptionHandlingConfigurer http.exceptionHandling(); //configures add HeadersConfigurer http.headers(); //configures add SessionManagementConfigurer http.sessionManagement(); //configure add SecurityContextConfigurer http.securityContext(); //configure add RequestCacheConfigurer http.requestCache(); ///configure add AnonymousConfigurer http.anonymous(); ///configure add ServletApiConfigurer http.servletApi(); //自定义默认config http.apply(new DefaultLoginPageConfigurer<>()); //configure LogoutConfigurer http.logout(); }
<12>
org.springframework.security.config.annotation.web.configurers.HeadersConfigurer#configure
@Override public void configure(H http) { //创建一个HeaderFilter HeaderWriterFilter headersFilter = createHeaderWriterFilter(); //添加到HttpSecurityFilter http.addFilter(headersFilter); }
<13>
org.springframework.security.config.annotation.web.builders.HttpSecurity#performBuild
@Override protected DefaultSecurityFilterChain performBuild() { //将httpSecurity filter排序 this.filters.sort(OrderComparator.INSTANCE); List<Filter> sortedFilters = new ArrayList<>(this.filters.size()); for (Filter filter : this.filters) { sortedFilters.add(((OrderedFilter) filter).filter); } //requestMatcher 为匹配条件 DefaultSecurityFilterChain 包装起来 管理所有Filter return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters); }
<14>
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#setApplicationContext
@Autowired public void setApplicationContext(ApplicationContext context) { this.context = context; ObjectPostProcessor<Object> objectPostProcessor = context.getBean(ObjectPostProcessor.class); LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(context); this.authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(objectPostProcessor, passwordEncoder); this.localConfigureAuthenticationBldr = new DefaultPasswordEncoderAuthenticationManagerBuilder( objectPostProcessor, passwordEncoder) { @Override public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) { WebSecurityConfigurerAdapter.this.authenticationBuilder.eraseCredentials(eraseCredentials); return super.eraseCredentials(eraseCredentials); } @Override public AuthenticationManagerBuilder authenticationEventPublisher( AuthenticationEventPublisher eventPublisher) { WebSecurityConfigurerAdapter.this.authenticationBuilder.authenticationEventPublisher(eventPublisher); return super.authenticationEventPublisher(eventPublisher); } }; }
<15>
跟WebSecurityConfigurerAdapter 一样 以下3个方法都是add不同的config 执行不同的初始化逻辑
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder { private PasswordEncoder defaultPasswordEncoder; /** * Creates a new instance * @param objectPostProcessor the {@link ObjectPostProcessor} instance to use. */ DefaultPasswordEncoderAuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor, PasswordEncoder defaultPasswordEncoder) { super(objectPostProcessor); this.defaultPasswordEncoder = defaultPasswordEncoder; } @Override public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication() throws Exception { return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder); } @Override public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception { return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder); } @Override public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService( T userDetailsService) throws Exception { return super.userDetailsService(userDetailsService).passwordEncoder(this.defaultPasswordEncoder); } }
<16>
org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder#performBuild
@Override protected ProviderManager performBuild() throws Exception { if (!isConfigured()) { this.logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null."); return null; } //通过ProviderManager 统一管理providers authenticationProviders 都可以定制 ProviderManager providerManager = new ProviderManager(this.authenticationProviders, this.parentAuthenticationManager); if (this.eraseCredentials != null) { providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials); } if (this.eventPublisher != null) { providerManager.setAuthenticationEventPublisher(this.eventPublisher); } //依赖注入 providerManager = postProcess(providerManager); return providerManager; }