咱們知道Spring Security的核心實現原理都是從filter開始的,Spring Security經過構造層層filter來實現登陸跳轉、權限驗證,角色管理等功能。本章經過剖析Spring Security的核心源碼來講明Spring Security的filter是如何開始構造並運行的。java
每每咱們定義一個Spring Security程序都是經過配置一個WebSecurityConfig類開始的,簡單代碼以下:web
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(final HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and().formLogin(); } }
經過以上代碼一個簡單的Spring Security應用程序就能成功執行了,該程序能攔截除了/login路徑之外的全部請求到登陸頁面。
咱們能夠看到以上代碼並無任何顯示聲明filter的語句,那麼Spring Security是如何經過上述代碼生成filter的呢?下面就由我來一層層解剖Spring Security的源碼來講明。spring
咱們注意到如上代碼有個@EnableWebSecurity註解,進入該註解查看設計模式
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented #注意這裏! @Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class}) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { boolean debug() default false; }
咱們能夠看到如上該註解導入了WebSecurityConfiguration類,進入該類查看:session
@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; public WebSecurityConfiguration() { } @Bean public static DelegatingApplicationListener delegatingApplicationListener() { return new DelegatingApplicationListener(); } ........ }
WebSecurityConfiguration類是做爲一個Spring配置源,同時定義了許多bean,這裏重點看以下這個方法:app
@Autowired( required = false ) public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { // 這段代碼初始化webSecurity this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); if (this.debugEnabled != null) { this.webSecurity.debug(this.debugEnabled); } // webSecurityConfigurers該屬性是經過@Value註解注入的 Collections.sort(webSecurityConfigurers, WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null; Object previousConfig = null; Iterator var5; SecurityConfigurer config; for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) { config = (SecurityConfigurer)var5.next(); 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; } // 將webSecurityConfigurers依次放入webSecurity var5 = webSecurityConfigurers.iterator(); while(var5.hasNext()) { config = (SecurityConfigurer)var5.next(); this.webSecurity.apply(config); } this.webSecurityConfigurers = webSecurityConfigurers; }
總結下該方法所作的主要操做:ide
這裏有一個重要的接口SecurityConfigurer接口,該接口代碼以下:post
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> { void init(B var1) throws Exception; void configure(B var1) throws Exception; }
回顧上面咱們編寫的WebSecurityConfig配置類,也有一個configure方法,那麼咱們猜想WebSecurityConfig類是否是也實現了SecurityConfigurer接口呢?答案是是的,咱們能夠看WebSecurityConfig類的類圖
能夠看到WebSecurityConfig類實現了SecurityConfigurer接口。
所以webSecurityConfigurers屬性經過依賴注入包含了WebSecurityConfig類,經過上述第3條操做將咱們配置的WebSecurityConfig類和WebSecurity類關聯起來。ui
到這裏咱們知道了WebSecurityConfiguration類調用上述方法將咱們配置的WebSecurityConfig類用WebSecurity類的apply方法關聯起來,那麼咱們詳細看看WebSecurity類的apply方法:this
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(this.objectPostProcessor); configurer.setBuilder(this); // 繼續調用該類的add方法 this.add(configurer); return configurer; } private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception { Assert.notNull(configurer, "configurer cannot be null"); // 獲取class屬性 Class<? extends SecurityConfigurer<O, B>> clazz = configurer.getClass(); // 獲取LinkedHashMap LinkedHashMap var3 = this.configurers; synchronized(this.configurers) { if (this.buildState.isConfigured()) { throw new IllegalStateException("Cannot apply " + configurer + " to already built object"); } else { List<SecurityConfigurer<O, B>> configs = this.allowConfigurersOfSameType ? (List)this.configurers.get(clazz) : null; if (configs == null) { configs = new ArrayList(1); } ((List)configs).add(configurer); // 將configurer放入一個LinkedHashMap中 this.configurers.put(clazz, configs); if (this.buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } } }
從上述代碼可知,實際上就是將WebSecurityConfig類放入了WebSecurity類的一個LinkedHashMap中,該LinkedHashMap在WebSecurity中屬性名爲configurers。
咱們繼續回到WebSecurityConfiguration類,查看它的另一個重要的方法:
@Bean( name = {"springSecurityFilterChain"} ) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty(); if (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() { }); this.webSecurity.apply(adapter); } return (Filter)this.webSecurity.build(); }
該方法即爲Spring Security構建Filter的核心方法,經過webSecurity的build方法構建了Spring Security的Filter。
咱們繼續查看WebSecurity類的build方法:
public final O build() throws Exception { if (this.building.compareAndSet(false, true)) { this.object = this.doBuild(); return this.object; } else { throw new AlreadyBuiltException("This object has already been built"); } }
實際上調用了上層的doBuild:
protected final O doBuild() throws Exception { LinkedHashMap var1 = this.configurers; synchronized(this.configurers) { this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING; this.beforeInit(); this.init(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING; this.beforeConfigure(); this.configure(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING; O result = this.performBuild(); this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT; return result; } }
這裏主要看WebSecurity的init方法和performBuild方法,首先看init方法
private void init() throws Exception { // this.getConfigurers()該方法實際上獲取WebSecurity中LinkedHashMap中的Value值集合 Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers(); Iterator var2 = configurers.iterator(); SecurityConfigurer configurer; while(var2.hasNext()) { configurer = (SecurityConfigurer)var2.next(); // 調用SecurityConfigurer的init方法 configurer.init(this); } var2 = this.configurersAddedInInitializing.iterator(); while(var2.hasNext()) { configurer = (SecurityConfigurer)var2.next(); configurer.init(this); } }
經過該代碼可知,該方法首先獲取WebSecurity中的LinkedHashMap中的Value值集合,再對Value值進行遍歷並執行其中的init方法,從上面的代碼分析咱們知道WebSecurity中的LinkedHashMap實際存的就是WebSecurityConfig,這段代碼將會調用WebSecurityConfig的init方法,而WebSecurityConfig的init方法來自於它的父類WebSecurityConfigurerAdapter,該init方法代碼以下:
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> { public void init(final WebSecurity web) throws Exception { // 獲取HttpSecurity final HttpSecurity http = this.getHttp(); // 將HttpSecurity放入WebSecurity中 web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() { public void run() { FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class); web.securityInterceptor(securityInterceptor); } }); } protected final HttpSecurity getHttp() throws Exception { if (this.http != null) { return this.http; } else { DefaultAuthenticationEventPublisher eventPublisher = (DefaultAuthenticationEventPublisher)this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher()); this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher); AuthenticationManager authenticationManager = this.authenticationManager(); this.authenticationBuilder.parentAuthenticationManager(authenticationManager); this.authenticationBuilder.authenticationEventPublisher(eventPublisher); Map<Class<? extends Object>, Object> sharedObjects = this.createSharedObjects(); this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects); if (!this.disableDefaults) { ((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)this.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); Iterator var6 = defaultHttpConfigurers.iterator(); while(var6.hasNext()) { AbstractHttpConfigurer configurer = (AbstractHttpConfigurer)var6.next(); this.http.apply(configurer); } } // 調用本類的configure方法 this.configure(this.http); return this.http; } } // 模板方法設計模式,子類WebSecurityConfig將會覆蓋該方法 protected void configure(HttpSecurity http) throws Exception { this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity)."); ((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic(); } }
以上代碼最終仍是實際調用了咱們寫的WebSecurityConfig類的configure方法。
仔細觀察以上代碼,咱們發現有一條語句web.addSecurityFilterChainBuilder(http),該語句將構建的HttpSecurity放入WebSecurity類中,如下是該方法源碼:
public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) { this.securityFilterChainBuilders.add(securityFilterChainBuilder); return this; }
實際上就是將HttpSecurity放入了WebSecurity的一個list集合裏,該list集合屬性名爲securityFilterChainBuilders。
到目前爲止,咱們終於知道咱們編寫的WebSecurityConfig類的configure方法是如何被調用的了,可是仍有許多疑問沒解開,好比HttpSecurity類的做用,Spring Security是如何經過HttpSecurity類構建一條攔截器鏈等。
這裏咱們先不分析HttpSecurity類的具體實現,再來看看WebSecurity的init方法執行後所執行的performBuild方法,該方法源碼以下:
protected Filter performBuild() throws Exception { Assert.state(!this.securityFilterChainBuilders.isEmpty(), () -> { return "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 = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize); Iterator var3 = this.ignoredRequests.iterator(); while(var3.hasNext()) { RequestMatcher ignoredRequest = (RequestMatcher)var3.next(); securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest, new Filter[0])); } / 遍歷securityFilterChainBuilders集合 var3 = this.securityFilterChainBuilders.iterator(); while(var3.hasNext()) { SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var3.next(); // 執行securityFilterChainBuilders集合單位的build方法,返回一個SecurityFilterChain類,並加入List<SecurityFilterChain>中 securityFilterChains.add(securityFilterChainBuilder.build()); } // 將List<SecurityFilterChain>類構建成一個FilterChainProxy代理類 FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (this.httpFirewall != null) { filterChainProxy.setFirewall(this.httpFirewall); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; if (this.debugEnabled) { this.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); } this.postBuildAction.run(); // 返回FilterChainProxy代理類 return (Filter)result; }
該方法執行的操做主要以下:
到這裏總的過程就很是明瞭了,實際上Spring Security的頂層filter就是一個FilterChainProxy類,而HttpSecurity主要用於註冊和實例化各類filter
到這裏就分紅了兩路,一路是HttpSecurity的build方法構建SecurityFilterChain類的原理,一路是FilterChainProxy類的做用,咱們先從FilterChainProxy類開始
當請求到達的時候,FilterChainProxy會調用dofilter()方法,會遍歷全部的SecurityFilterChain,對匹配到的url,則一一調用SecurityFilterChain中的filter作認證受權。FilterChainProxy的dofilter()中調用了doFilterInternal()方法,以下:
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FirewalledRequest fwRequest = firewall .getFirewalledRequest((HttpServletRequest) request); HttpServletResponse fwResponse = firewall .getFirewalledResponse((HttpServletResponse) response); // 獲取請求對應的filter列表 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; } VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters); // 執行每一個filter vfc.doFilter(fwRequest, fwResponse); } // 經過遍歷filterChains,調用SecurityFilterChain的matches方法,判斷當前的請求對應哪些filter,返回匹配的filter列表 private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; }
咱們理清了FilterChainProxy類的做用,那麼這些SecurityFilterChain是從哪來的呢?從上節可知SecurityFilterChain是由HttpSecurity的build方法生成的,下面咱們分析下HttpSecurity類
HttpSecurity與WebSecurity同樣,都繼承了AbstractConfiguredSecurityBuilder類,而WebSecurity的build和doBuild方法和LinkedHashMap屬性,均來自AbstractConfiguredSecurityBuilder,故HttpSecurity的build方法代碼與WebSecurity的相同,區別在於LinkedHashMap存儲的東西不一樣,HttpSecurity正是經過如此來生成SecurityFilterChain類的。
下面咱們來看HttpSecurity構建filter的幾個常見方法:
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception { ApplicationContext context = this.getContext(); return ((ExpressionUrlAuthorizationConfigurer)this.getOrApply(new ExpressionUrlAuthorizationConfigurer(context))).getRegistry(); } public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception { return (FormLoginConfigurer)this.getOrApply(new FormLoginConfigurer()); }
都調用了getOrApply方法,再來看getOrApply方法,又調用了其中的apply方法
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer) throws Exception { C existingConfig = (SecurityConfigurerAdapter)this.getConfigurer(configurer.getClass()); return existingConfig != null ? existingConfig : this.apply(configurer); } public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(this.objectPostProcessor); configurer.setBuilder(this); this.add(configurer); return configurer; }
apply方法又調用了add方法,這裏的add方法最終仍是將該configurer加入了linkedHashMap中
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 = configurer.getClass(); LinkedHashMap var3 = this.configurers; synchronized(this.configurers) { if (this.buildState.isConfigured()) { throw new IllegalStateException("Cannot apply " + configurer + " to already built object"); } else { List<SecurityConfigurer<O, B>> configs = this.allowConfigurersOfSameType ? (List)this.configurers.get(clazz) : null; if (configs == null) { configs = new ArrayList(1); } ((List)configs).add(configurer); this.configurers.put(clazz, configs); if (this.buildState.isInitializing()) { this.configurersAddedInInitializing.add(configurer); } } } }
故HttpSecurity在構建filter的過程當中,本質仍是將形如ExpressionUrlAuthorizationConfigurer、FormLoginConfigurer等類加入了它的LinkedHashMap中。
那麼將這些Configurer類存入LinkedHashMap的做用又是什麼?咱們回憶上面WebSecurity類的doBuild方法,咱們知道HttpSecurity類調用的doBuild方法與WebSecurity類同樣,而經過觀察WebSecurity類doBuild方法裏this.init();this.configure();這些語句的具體實現,實際就是調用其LinkedHashMap中的元素的init方法和configure方法。
咱們如今來查看其中一個ExpressionUrlAuthorizationConfigurer類的configure方法的詳細代碼:
public void configure(H http) throws Exception { FilterInvocationSecurityMetadataSource metadataSource = this.createMetadataSource(http); if (metadataSource != null) { FilterSecurityInterceptor securityInterceptor = this.createFilterSecurityInterceptor(http, metadataSource, (AuthenticationManager)http.getSharedObject(AuthenticationManager.class)); if (this.filterSecurityInterceptorOncePerRequest != null) { securityInterceptor.setObserveOncePerRequest(this.filterSecurityInterceptorOncePerRequest); } securityInterceptor = (FilterSecurityInterceptor)this.postProcess(securityInterceptor); // 將Filter加入了HttpSecurity的Filters集合中 http.addFilter(securityInterceptor); http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); } }
最後來看看HttpSecruity的performBuild()方法:
protected DefaultSecurityFilterChain performBuild() throws Exception { Collections.sort(this.filters, this.comparator); return new DefaultSecurityFilterChain(this.requestMatcher, this.filters); }
實際上就是經過Filters集合構建了SecurityFilterChain。
從上面代碼可總結出,HttpSecurity內部維護一個Filter列表,而HttpSecurity調用形如authorizeRequests(),formLogin()等方法實際上就是將filter添加入它的列表當中,最後經過performBuild()方法構建出SecurityFilterChain,至此HttpSecurity構建filter的總過程就完成了。
到目前爲止,咱們終於知道Spring Security是如何一步步的構建和初始化filter的了,咱們最後再來簡單總結下構建過程: