Spring Security 啟動過程分析
以github登入為例,首先建立一個Spring Boot工程,版本為2.1.0.RELEASE
,工程結構如下圖:
image.png
pom.xml
如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-client</artifactId> <version>5.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
啟動類SecurityApplication.java
:
/** * @author iHelin */@RestController@SpringBootApplicationpublic class SecurityApplication { public static void main(String[] args) { SpringApplication.run(SecurityApplication.class, args); } @GetMapping({"/", "/user"}) public Object get() { OAuth2AuthenticationToken authentication = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); OAuth2User principal = authentication.getPrincipal(); return principal.getAttributes(); } }
SercurityConfig.java
:
/** * @author iHelin * @date 2018-11-30 15:47 */@EnableWebSecurity(debug = true)public class SercurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests().anyRequest().authenticated().and() .oauth2Login(); } }
配置檔案application.properties
server.port=8080logging.level.org.springframework.security=debug logging.level.org.springframework.boot.autoconfigure.security=debug spring.security.oauth2.client.registration.github.client-id=xxxxxx spring.security.oauth2.client.registration.github.client-secret=xxxxxx
從@EnableWebSecurity
開始說起
SercurityConfig是一個配置類,它繼承了WebSecurityConfigurerAdapter
,並標明瞭@EnableWebSecurity(debug = true)
註解,檢視這個註解發現,裡面又匯入(import)了WebSecurityConfiguration.class
這個配置類,如下圖:
image.png
WebSecurityConfiguration是一個自動配置類,它的主要作用建立過濾器鏈(securityFilterChains)並完成安全配置工作,而這一系列過程主要是透過webSecurity完成的。
系統啟動時Spring上下文會首先呼叫它setFilterChainProxySecurityConfigurer
方法進行webSecurity的初始化,這一步透過反射完成(當然,這不是我們的重點)。然後再呼叫springSecurityFilterChain
進行webSecurity的配置,具體步驟如下:
首先進入springSecurityFilterChain方法
image.png
接著呼叫org.springframework.security.config.annotation.AbstractSecurityBuilder#build
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"); }
cas操作進入if語句,進入關鍵的org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild
@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和performBuild方法,注意現在我們的主語還是webSecurity。首先看init方法:
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); } }
這裡主要看第一個for迴圈,裡面會進行一些配置的初始化,其中會有一個我們繼承的WebSecurityConfigurerAdapter
的代理,其實也就是我們自己定義的安全配置類SercurityConfig
,呼叫其init方法:
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); } }); }
看下getHttp:
protected final HttpSecurity getHttp() throws Exception { ... 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 ... } configure(http); return http; }
裡面首先進行預設的配置,這裡新增了一個Filter:WebAsyncManagerIntegrationFilter
,繼續向下執行,會執行configure方法,它是一個模板方法,也就是這裡會執行我們配置類裡面覆蓋的configure方法,這裡就完成了httpSecurity的初始化。
以上步驟都只是webSecurity的init操作,也就是建立了許多的配置器,接下來進入webSecurity的performBuild方法使配置生效,具體過程是呼叫httpSecurity的config方法,裡面會呼叫上面建立的眾多配置器的configure方法,其目的是向過濾器鏈新增各種Filter,最後還會呼叫performBuild方法對過濾器進行排序,建立DefaultSecurityFilterChain過濾器鏈,這裡以ExceptionHandlingConfigurer為例。
public void configure(H http) throws Exception { AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http); ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter( entryPoint, getRequestCache(http)); AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http); exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler); exceptionTranslationFilter = postProcess(exceptionTranslationFilter); http.addFilter(exceptionTranslationFilter); }
關鍵看最後的addFilter方法
public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."); } this.filters.add(filter); return this; }
最終向httpSecurity物件的filters中新增filter。
然後再呼叫httpSecurity的performBuild方法對filters進行排序:
protected DefaultSecurityFilterChain performBuild() throws Exception { Collections.sort(filters, comparator); return new DefaultSecurityFilterChain(requestMatcher, filters); }
最後返回了一個DefaultSecurityFilterChain物件,至此http的配置宣告完成。再回到webSecurity的performBuild方法,它根據httpSecurity返回的securityFilterChain建立了一個securityFilterChains。
protected Filter performBuild() throws Exception { ... for (RequestMatcher ignoredRequest : ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); ... Filter result = filterChainProxy; ... return result;
作者:iHelin
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2144/viewspace-2818772/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring啟動過程——原始碼分析Spring原始碼
- Spring Boot原始碼分析-啟動過程Spring Boot原始碼
- Spring MVC 啟動過程原始碼分析SpringMVC原始碼
- Spring啟動過程——原始碼分析(finishBeanFactoryInitialization(beanFactory))Spring原始碼Bean
- Spring啟動過程(一)Spring
- Spring Boot 啟動過程Spring Boot
- Spring啟動過程原始碼分析基本概念Spring原始碼
- Spring security(三)---認證過程Spring
- Linux 啟動過程分析Linux
- Liferay 啟動過程分析
- 精盡Spring Boot原始碼分析 - SpringApplication 啟動類的啟動過程Spring Boot原始碼APP
- Eureka Server啟動過程分析Server
- Spring Security系列之授權過程(七)Spring
- Spring Security系列之認證過程(六)Spring
- Spring security(五)-完美許可權管理系統(授權過程分析)Spring
- spring原始碼閱讀--容器啟動過程Spring原始碼
- 淺析Spring Framework框架容器啟動過程SpringFramework框架
- Netty NioEventLoop 啟動過程原始碼分析NettyOOP原始碼
- Spring Security原始碼分析八:Spring Security 退出Spring原始碼
- 漲姿勢:Spring Boot 2.x 啟動全過程原始碼分析Spring Boot原始碼
- Spring Security原始碼分析九:Spring Security Session管理Spring原始碼Session
- 淺析Spring Security 的認證過程及相關過濾器Spring過濾器
- React Native Android 原始碼分析之啟動過程React NativeAndroid原始碼
- Android系統原始碼分析--Activity啟動過程Android原始碼
- Service啟動過程
- SpringBoot啟動過程Spring Boot
- Windows 啟動過程Windows
- Spring Security系列之核心過濾器原始碼分析(四)Spring過濾器原始碼
- Spring Security原始碼分析十一:Spring Security OAuth2整合JWTSpring原始碼OAuthJWT
- (五)SpringBoot啟動過程的分析-重新整理ApplicationContextSpring BootAPPContext
- (四)SpringBoot啟動過程的分析-預處理ApplicationContextSpring BootAPPContext
- Hive原始碼分析(1)——HiveServer2啟動過程Hive原始碼Server
- App 啟動過程(含 Activity 啟動過程) | 安卓 offer 收割基APP安卓
- Angular的啟動過程Angular
- Android App啟動過程AndroidAPP
- SpringBoot 系列-啟動過程Spring Boot
- jmeter 啟動過程剖析JMeter
- iOS App啟動過程iOSAPP