本篇主要講解 SpringSecurity初始化流程的源碼部分,包括核心的 springSecurityFilterChain 是如何建立的,以及在介紹哪裏能夠擴展個性化的配置,SpringSecurity源碼實際上是蠻可貴 各類Builder Configure 看得真的頭疼!html
SpringSecurity 的核心功能主要包括:java
認證 (你是誰) 受權 (你能幹什麼) 攻擊防禦 (防止僞造身份)web
其核心就是一組過濾器鏈,項目啓動後將會自動配置,本篇也會涉及過濾器鏈是如何自動初始化的。spring
SecurityContextPersistenceFilter 是最前面的一個filter 請求到它時候會去檢查 根據sessionId找到session 判斷session 中是否存在 SecurityContext 在 則將 SecurityContext 存入當前的線程中去 響應的時候,看當前線程是否有SecurityContext ,若是有 放入到session中去 這樣不一樣的請求都能拿到相同的 用戶認證信息。session
UsernamePasswordAuthenticationFilter 該過濾器是處理表單登陸的,經過表單登陸提交的認證都會通過它處理app
SocialAuthenticationFilter 好比這個就是社交登陸使用的Filter 詳細能夠看我另一篇 SpringSocial 實現第三方QQ登陸SpringSocial 實現第三方QQ登陸ide
綠色的過濾器都是可配置的,其餘顏色的都不行!post
若是是SpringBoot項目只要你依賴了SpringSecurity相關依賴依然會有自動配置類 SecurityAutoConfiguration 生效 它會導入 WebSecurityEnableConfiguration測試
@EnableWebSecurity將會是咱們本篇的主要切入點ui
該註解 它是初始化Spring Security的入口 .
打開@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; }
該註解類經過@Configuration和@Import配合使用引入了一個配置類(WebSecurityConfiguration)和兩個ImportSelector(SpringWebMvcImportSelector,OAuth2ImportSelector),咱們重點關注下WebSecurityConfiguration,它是Spring Security的核心
打開WebSecurityConfiguration 它是一個配置類,主要看 springSecurityFilterChain()方法,它就是初始化 springSecurityFilterChain的核心方法
/** * Creates the Spring Security Filter Chain * @return the {@link Filter} that represents the security filter chain * @throws Exception */ @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(); }
@Bean註解name屬性值AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME就是XML中定義的springSecurityFilterChain
從源碼中知道過濾器經過最後的 webSecurity.build()建立,webSecurity的類型爲:WebSecurity,它在 setFilterChainProxySecurityConfigurer方法中優先被建立了:
@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); } webSecurityConfigurers.sort(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; }
從代碼中能夠看到,它是直接被new出來的:
webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor));
setFilterChainProxySecurityConfigurer 該方法的webSecurityConfigurers 參數是經過@Value注入的
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
AutowiredWebSecurityConfigurersIgnoreParents的 getWebSecurityConfigurers() 以下,就是獲取全部的 WebSecurityConfigurer的類型的配置類 而一般 咱們經過繼承 WebSecurityConfigurerAdapter 來自定義WebSecurityConfigurer
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() { List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>(); Map<String, WebSecurityConfigurer> beansOfType = beanFactory .getBeansOfType(WebSecurityConfigurer.class); for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) { webSecurityConfigurers.add(entry.getValue()); } return webSecurityConfigurers; }
再回到setFilterChainProxySecurityConfigurer方法 下面有一段這樣的代碼 ,對於上面獲取的全部的WebSecurityConfigurer類型 循環執行 webSecurity的apply方法
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { webSecurity.apply(webSecurityConfigurer); }
webSecurity集成AbstractConfiguredSecurityBuilder 它提供apply方法 再其內部調用add方法
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; }
add(configurer),主要就是將其傳入的WebSecurityConfigurer存入到 LinkedHashMap configures中, 主要代碼 this.configurers.put(clazz, configs);
private <C extends SecurityConfigurer<O, B>> void add(C configurer) { 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<>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } }
當全部的 WebSecurityConfigurer 類型的配置 所有應用到 WebSecurity中去後 setFilterChainProxySecurityConfigurer方法也就結束了
回到建立過濾器鏈的方法 springSecurityFilterChain()
它會判斷咱們剛剛的webSecurityConfigurers是否存在,不存在就新建一個,而後執行 webSecurity.build() 重要!
@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(); }
最終內部會有下面這段代碼, 主要關注 init() configure() 和 performBuild() 這三個方法
@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; } }
init() 內部循環遍歷 全部的 WebSecurityConfigurer ,它會執行到 WebSecurityConfigurerAdapter的
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); } }
configurer.init((B) this)
它只要完成兩件重要的事情:
初始化HttpSecurity對象(注意它和WebSecurity不同 ); 設置HttpSecurity對象添加至WebSecurity的securityFilterChainBuilders列表中;
public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); }
初始化HttpSecurity對象在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); authenticationBuilder.authenticationEventPublisher(eventPublisher); Map<Class<?>, 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; }
從代碼中能夠了解,HttpSecurity是直接被new出來的,在建立HttpSecurity以前,首先初始化了AuthenticationManagerBuilder對象,這裏有段代碼很重要就是: AuthenticationManager authenticationManager = authenticationManager();,它建立AuthenticationManager實例,打開authenticationManager()方法:
默認實現是在 WebSecurityConfigurerAdapter 中
protected void configure(AuthenticationManagerBuilder auth) throws Exception { this.disableLocalConfigureAuthenticationBldr = true; }
一、個性化配置入口之configure(AuthenticationManagerBuilder auth)
咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置AuthenticationManagerBuilder。
以下是本身繼承WebSecurityConfigurerAdapter 重寫 configure(AuthenticationManagerBuilder auth),實現個性化的第一個配置入口
/** * @author johnny * @create 2020-01-18 下午6:40 **/ @Configuration @Slf4j public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { super.configure(auth); log.info("【測試 定製化入口 configure(AuthenticationManagerBuilder auth) 的執行 】"); } }
構建完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();
我挑一個默認的方法展開看一下好比 會話管理的sessionManagement(),內部就是去建立SessionManagementConfigurer並應用它
public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception { return getOrApply(new SessionManagementConfigurer<>()); }
getOrApply 最有一句代碼 return apply(configurer);
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); }
apply(configurer) 注意這裏的 configurer傳入的是SessionManagementConfigurer
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); add(configurer); return configurer; }
最終又調用了 add(configurer); 這不過這裏是給 HttpSecurity的 configurers 配置初始的,上面是配置的WebSecurity的configurers, 不要混淆,最終這些configurers會被一個個建立成 對應的過濾器Filter的 詳細在後面有說明
private <C extends SecurityConfigurer<O, B>> void add(C configurer) { 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<>(1); } configs.add(configurer); this.configurers.put(clazz, configs); if (buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } }
以下圖:爲HttpSecurity添加了不少默認的配置
回到 getHttp()方法
最後調用configure(http);,這又是一個可個性化的配置入口,它的默認實現是:WebSecurityConfigurerAdapter提供的
默認的配置是攔截全部的請求須要認證以後才能訪問,若是沒有認證,會自動生成一個認證表單要求輸入用戶名和密碼。
protected void configure(HttpSecurity http) throws Exception { logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }
二、個性化配置入口之configure(HttpSecurity http) 咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置HttpSecurity。
OK,目前爲止HttpSecurity已經被初始化,接下去須要設置HttpSecurity對象添加至WebSecurity的securityFilterChainBuilders列表中:
public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); }
當全部的WebSecurityConfigurer的init方法被調用以後,webSecurity.init()工做就結束了
接下去調用了webSecurity.configure(),該方法一樣是在AbstractConfiguredSecurityBuilder中實現的:
private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this); } }
它的主要工做是迭代調用全部WebSecurityConfigurer的configurer方法,參數是WebSeucrity自己,這又是另一個重要的個性化入口:
三、個性化配置入口之configure(WebSecurity web) 咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置WebSecurity。
至此,三個重要的個性化入口都已經被調用,即在實現WebSecurityConfigurerAdapter常常須要重寫的:
一、configure(AuthenticationManagerBuilder auth); 二、configure(WebSecurity web); 三、configure(HttpSecurity http);
回到webSecurity構建過程,接下去重要的的調用:
O result = performBuild();
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) { 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; }
首先計算出chainSize,也就是ignoredRequests.size() + securityFilterChainBuilders.size();,若是你不配置ignoredRequests,那就是securityFilterChainBuilders.size(),也就是HttpSecurity的個數,其本質上就是你一共配置幾個WebSecurityConfigurerAdapter,由於每一個WebSecurityConfigurerAdapter對應一個HttpSecurity,而所謂的ignoredRequests就是FilterChainProxy的請求,默認是沒有的,若是你須要條跳過某些請求不須要認證或受權,能夠以下配置:
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/statics/**"); }
在上面配置中,全部以/statics開頭請求都將被FilterChainProxy忽略。
securityFilterChains.add(securityFilterChainBuilder.build()); 這一行就是初始化全部的過濾器,記得上面有段代碼以下,將HttpSecurity設置到WebSecurity的 securityFilterChainBuilder中,上面就是調用HttpSecurity.build()方法,初始化全部的 HttpSecurity的過濾器鏈
public void init(final WebSecurity web) throws Exception { final HttpSecurity http = getHttp(); web.addSecurityFilterChainBuilder(http).postBuildAction(() -> { FilterSecurityInterceptor securityInterceptor = http .getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); }); }
依然來到 doBuild()方法,只不過此次是執行的 HttpSecurity的
@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; } }
重點查看 configure()該方法 會調用對應的 過濾器配置的configure() 如 再內部建立 SessionManagementFilter 最後添加到HttpSecurity中,也就是拿 HttpSecurity的configures 一個個建立出對應的過濾器
@Override public void configure(H http) { SecurityContextRepository securityContextRepository = http .getSharedObject(SecurityContextRepository.class); SessionManagementFilter sessionManagementFilter = new SessionManagementFilter( securityContextRepository, getSessionAuthenticationStrategy(http)); if (this.sessionAuthenticationErrorUrl != null) { sessionManagementFilter.setAuthenticationFailureHandler( new SimpleUrlAuthenticationFailureHandler( this.sessionAuthenticationErrorUrl)); } InvalidSessionStrategy strategy = getInvalidSessionStrategy(); if (strategy != null) { sessionManagementFilter.setInvalidSessionStrategy(strategy); } AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler(); if (failureHandler != null) { sessionManagementFilter.setAuthenticationFailureHandler(failureHandler); } AuthenticationTrustResolver trustResolver = http .getSharedObject(AuthenticationTrustResolver.class); if (trustResolver != null) { sessionManagementFilter.setTrustResolver(trustResolver); } sessionManagementFilter = postProcess(sessionManagementFilter); http.addFilter(sessionManagementFilter); if (isConcurrentSessionControlEnabled()) { ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http); concurrentSessionFilter = postProcess(concurrentSessionFilter); http.addFilter(concurrentSessionFilter); } }
當doBuild()中的 configure();執行完畢後 的會獲得以下HttpSecurity能夠看到它內部的filters已經所有建立完畢
回到doBuild()方法 該方中有 performBuild() 調用HttpSecurity的 performBuild(),默認實現以下,先對上面全部的過濾器進行排序,使用的是 FilterComparator() 進行排序的,這裏不展開了,反正就是會排序成文章開始的那張圖上面的順序
@Override protected DefaultSecurityFilterChain performBuild() { filters.sort(comparator); return new DefaultSecurityFilterChain(requestMatcher, filters); }
最後返回的是SecurityFilterChain的默認實現DefaultSecurityFilterChain。
構建完全部SecurityFilterChain後,建立最爲重要的FilterChainProxy實例,
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
至此Spring Security 初始化完成,包括springSecurityFilterChain初始化,咱們經過繼承WebSecurityConfigurerAdapter來代達到個性化配置目的,文中提到了三個重要的個性化入口,而且WebSecurityConfigurerAdapter是能夠配置多個的,其對應的接口就是會存在多個SecurityFilterChain實例,可是它們人仍然在同一個FilterChainProxy中,經過RequestMatcher來匹配並傳入到對應的SecurityFilterChain中執行請求。
重要的個性化入口都是哪裏調用的 已經在上面初始化 springSecurityFilterChain 源碼中講解了,這裏知識總結一下
一、個性化配置入口之configure(AuthenticationManagerBuilder auth)
咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置AuthenticationManagerBuilder。
二、個性化配置入口之configure(HttpSecurity http) 咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置HttpSecurity。
三、個性化配置入口之configure(WebSecurity web) 咱們能夠經過繼承WebSecurityConfigurerAdapter並重寫該方法來個性化配置WebSecurity。
實現WebSecurityConfigurerAdapter常常須要重寫的:
一、configure(AuthenticationManagerBuilder auth); 二、configure(WebSecurity web); 三、configure(HttpSecurity http);
本篇主要講解了 1.SpringBoot對於SpringSecurity的自動配置的支持類SecurityAutoConfiguration, 2.核心註解@EnableWebSecurity 3. SpringSecurity的核心過濾器鏈 springSecurityFilterChain 的初始化流程的源碼 源碼部分仍是定下心來多看 加油!
我的博客地址: https://www.askajohnny.com 歡迎訪問! 本文由博客一文多發平臺 OpenWrite 發佈!
原文出處:https://www.cnblogs.com/askajohnny/p/12212544.html