按理說本身 new 出來的對象和容器是沒有關係的,可是在 Spring Security 框架中也 new 了不少對象出來,同樣也能夠被容器管理,那麼它是怎麼作到的?java
今天來和你們聊一個略微冷門的話題,Spring Security 中的 ObjectPostProcessor 究竟是幹嗎用的?web
本文是 Spring Security 系列第 32 篇,閱讀前面文章有助於更好的理解本文:spring
若是你們研究過鬆哥的微人事項目,就會發現裏邊的動態權限配置有這樣一行代碼:數據庫
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); return object; } }) .and() ... } }
這裏的 withObjectPostProcessor 到底該如何理解?後端
今天鬆哥就來和你們揭開謎題。跨域
咱們先來看下 ObjectPostProcessor 到底有啥做用,先來看一下這個接口的定義:安全
package org.springframework.security.config.annotation; public interface ObjectPostProcessor<T> { <O extends T> O postProcess(O object); }
接口中就只有一個 postProcess 方法。session
咱們再來看看 ObjectPostProcessor 的繼承關係:框架
兩個比較重要的實現類,其中 AutowireBeanFactoryObjectPostProcessor 值得一說,來看下 AutowireBeanFactoryObjectPostProcessor 的定義:前後端分離
final class AutowireBeanFactoryObjectPostProcessor implements ObjectPostProcessor<Object>, DisposableBean, SmartInitializingSingleton { AutowireBeanFactoryObjectPostProcessor( AutowireCapableBeanFactory autowireBeanFactory) { this.autowireBeanFactory = autowireBeanFactory; } @SuppressWarnings("unchecked") 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; } }
AutowireBeanFactoryObjectPostProcessor 的源碼很好理解:
因而可知,ObjectPostProcessor 的主要做用就是手動註冊實例到 Spring 容器中去(而且讓實例走一遍 Bean 的生命週期)。
正常來講,咱們項目中的 Bean 都是經過自動掃描注入到 Spring 容器中去的,然而在 Spring Security 框架中,有大量的代碼不是經過自動掃描注入到 Spring 容器中去的,而是直接 new 出來的,這樣作的本意是爲了簡化項目配置。
這些直接 new 出來的代碼,若是想被 Spring 容器管理該怎麼辦呢?那就得 ObjectPostProcessor 出場了。
接下來我隨便舉幾個框架中對象 new 的例子,你們看一下 ObjectPostProcessor 的做用:
HttpSecurity 初始化代碼(WebSecurityConfigurerAdapter#getHttp):
protected final HttpSecurity getHttp() throws Exception { if (http != null) { return http; } ... ... http = new HttpSecurity(objectPostProcessor, authenticationBuilder, sharedObjects); ... ... }
WebSecurity 初始化代碼(WebSecurityConfiguration#setFilterChainProxySecurityConfigurer):
public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { webSecurity = objectPostProcessor .postProcess(new WebSecurity(objectPostProcessor)); ... ... }
Spring Security 框架源碼中,隨處可見手動裝配。Spring Security 中,過濾器鏈中的全部過濾器都是經過對應的 xxxConfigure 來進行配置的,而全部的 xxxConfigure 都是繼承自 SecurityConfigurerAdapter,以下:
而在這些 xxxConfigure 的 configure 方法中,無一例外的都會讓他們各自配置的管理器去 Spring 容器中走一圈,例如 AbstractAuthenticationFilterConfigurer#configure 方法:
public void configure(B http) throws Exception { ... ... F filter = postProcess(authFilter); http.addFilter(filter); }
其餘的 xxxConfigurer#configure 方法也都有相似的實現,小夥伴們能夠自行查看,我就再也不贅述了。
直接將 Bean 經過自動掃描註冊到 Spring 容器很差嗎?爲何要搞成這個樣子?
在 Spring Security 中,因爲框架自己大量採用了 Java 配置,而且沒有將對象的各個屬性都暴露出來,這樣作的本意是爲了簡化配置。然而這樣帶來的一個問題就是須要咱們手動將 Bean 註冊到 Spring 容器中去,ObjectPostProcessor 就是爲了解決該問題。
一旦將 Bean 註冊到 Spring 容器中了,咱們就有辦法去加強一個 Bean 的功能,或者需修改一個 Bean 的屬性。
例如一開始提到的動態權限配置代碼:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource); return object; } }) .and() ... } }
權限管理自己是由 FilterSecurityInterceptor 控制的,系統默認的 FilterSecurityInterceptor 已經建立好了,並且我也沒辦法修改它的屬性,那麼怎麼辦呢?咱們能夠利用 withObjectPostProcessor 方法,去修改 FilterSecurityInterceptor 中的相關屬性。
上面這個配置生效的緣由之一是由於 FilterSecurityInterceptor 在建立成功後,會重走一遍 postProcess 方法,這裏經過重寫 postProcess 方法就能實現屬性修改,咱們能夠看下配置 FilterSecurityInterceptor 的方法(AbstractInterceptUrlConfigurer#configure):
abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C, H>, H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<C, H> { @Override public void configure(H http) throws Exception { FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); if (metadataSource == null) { return; } FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor( http, metadataSource, http.getSharedObject(AuthenticationManager.class)); if (filterSecurityInterceptorOncePerRequest != null) { securityInterceptor .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest); } securityInterceptor = postProcess(securityInterceptor); http.addFilter(securityInterceptor); http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); } }
能夠看到,securityInterceptor 對象建立成功後,仍是會去 postProcess 方法中走一遭。
看懂了上面的代碼,接下來我再舉一個例子小夥伴們應該一下就能明白:
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") ... .and() .formLogin() .withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>() { @Override public <O extends UsernamePasswordAuthenticationFilter> O postProcess(O object) { object.setUsernameParameter("name"); return object; } }) ... } }
在這裏,我把配置好的 UsernamePasswordAuthenticationFilter 過濾器再拎出來,修改一下用戶名的 key(正常來講,修改用戶名的 key 不用這麼麻煩,這裏主要是給你們演示 ObjectPostProcessor 的效果),修改完成後,之後用戶登陸時,用戶名就不是 username 而是 name 了。
好了,只要小夥伴們掌握了上面的用法,之後在 Spring Security 中,若是想修改某一個對象的屬性,可是殊不知道從哪裏下手,那麼不妨試試 withObjectPostProcessor!
小夥伴們若是以爲有收穫,記得點個在看鼓勵下鬆哥哦~