1.概述
-
spring security有參考的中文翻譯文檔https://springcloud.cc/spring-security-zhcn.html - 在學習
spring security的時候,參考的書籍並不是很多,線上的技術博文也是沒有系統的全面的介紹,不得不去參考源代碼,發費了大量時間和精力,而我試圖也通過一系列的文章,能結合自己的學習經歷,循序漸進的表達清楚自己所理解到的點。 - 所有的文章都是對源代碼的解讀,最后會給出一個小小的demo。
- 選用的
spring boot版本1.4.3.RELEASE,spring cloud版本Camden.SR4(后面有結合網關zuul做oauth2 認證授權介紹)。版本非spring 最新版本,選用的是平時使用最多版本。 - 文章是作為學習筆記性質為目的.當然如果文章能幫到有需要的朋友自然是特別開心和幸福的事情。有理解錯誤和不到位的點也希望各位指出,一起學習一起成長,一直沒有都沒敢寫公開技術文章,第一本人水平有限,第二是不希望因為自己的錯誤給別人造成誤導。
2.啟動
2.1 簡單的開始
從文檔中可知,spring引入我們的項目只需要分為兩步
- maven引入需要的包,spring boot中引入的spring security版本為4.1.4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 配置spring security(spring security官方文檔給出的例子,后面簡稱例子1)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
<span class="hljs-meta"><span class="hljs-meta">@Autowired</span></span>
<span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">public</span></span></span><span class="hljs-function"> </span><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">void</span></span></span><span class="hljs-function"> </span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">configureGlobal</span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">(AuthenticationManagerBuilder auth)</span></span></span><span class="hljs-function"> </span><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">throws</span></span></span><span class="hljs-function"> Exception </span></span>{
auth
.inMemoryAuthentication()
.withUser(<span class="hljs-string"><span class="hljs-string">"user"</span></span>).password(<span class="hljs-string"><span class="hljs-string">"password"</span></span>).roles(<span class="hljs-string"><span class="hljs-string">"USER"</span></span>);
}
}
2.2 簡單后面的繁瑣
我們潔簡的應用spring security到我們的項目,然后卻開啟了非常強大的功能,上面的例子,在內存中配置了一個用戶名為user,密碼為password,並且擁有USER角色的用戶。我們還是要探究一下,spring security到底為我們做了一些什么樣的工作。
我們的入手點只有兩個,@EnableWebSecurity注解和WebSecurityConfigurerAdapter這個適配器類。
2.2.1 @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 {
boolean debug() default false;
此處還是有必要簡單翻譯一下官方對於類的注釋,以便於更加清楚的理解注解的作用
注釋中提到兩個很重要的點,以及一個配置示例,
- 第一個點:
@EnableWebSecurity配置到擁有注解@Configuration的類上,就可以獲取到spring security的支持. - 第二個點:
WebSecurityConfigurer的子類可以擴展spring security的應用
由此可知@EnableWebSecurity使我們擁有spring security的能力。WebSecurityConfigurer 使我們能根據業務擴展我們的應用,而WebSecurityConfigurerAdapter是WebSecurityConfigurer 的一個適配器,必然也是做了很多默認的工作。后面我們會一一說到。
從以上可以稍微總結一下我們下一步需要探究的問題,
-
WebSecurityConfigurerAdapter到底為我們做了什么工作. - 上面注解的源代碼中Import了一個很重要的配置類
WebSecurityConfiguration怎樣組件我們的過濾器(或者說過濾器鏈)
2.2.2 spring security創建流程
- 如果我們忽略掉細節,只看最重要的步驟,大概如此.下面我們對每一個步驟來做相應的源代碼解釋
WebSecurityConfiguration中的 setFilterChainProxySecurityConfigurer()方法
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, //T1 使用@Value獲取到配置信息 @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
<span class="hljs-comment"><span class="hljs-comment">//T2 創建一個webSecurity 對象</span></span>
webSecurity = objectPostProcessor
.postProcess(<span class="hljs-keyword"><span class="hljs-keyword">new</span></span> WebSecurity(objectPostProcessor));
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (debugEnabled != <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>) {
webSecurity.debug(debugEnabled);
}
<span class="hljs-comment"><span class="hljs-comment">//T3對configures進行排序</span></span>
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
<span class="hljs-comment"><span class="hljs-comment">//T4對Order進行比較是否有相同的,由於前面進行了排序,只要比較前后有相同的就可以</span></span>
Integer previousOrder = <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>;
Object previousConfig = <span class="hljs-keyword"><span class="hljs-keyword">null</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">for</span></span> (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (previousOrder != <span class="hljs-keyword"><span class="hljs-keyword">null</span></span> && previousOrder.equals(order)) {
<span class="hljs-keyword"><span class="hljs-keyword">throw</span></span> <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> IllegalStateException(
<span class="hljs-string"><span class="hljs-string">"@Order on WebSecurityConfigurers must be unique. Order of "</span></span>
+ order + <span class="hljs-string"><span class="hljs-string">" was already used on "</span></span> + previousConfig + <span class="hljs-string"><span class="hljs-string">", so it cannot be used on "</span></span>
+ config + <span class="hljs-string"><span class="hljs-string">" too."</span></span>);
}
previousOrder = order;
previousConfig = config;
}
<span class="hljs-keyword"><span class="hljs-keyword">for</span></span> (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
<span class="hljs-comment"><span class="hljs-comment">//T5將配置信息配置到webSecurity中</span></span>
webSecurity.apply(webSecurityConfigurer);
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.webSecurityConfigurers = webSecurityConfigurers;
}
-
T1(注釋標記處)獲取配置信息
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}"
可以看一下autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()的源代碼
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
其實我們根據debug獲取到的來看,這個beansOfType 就是我們定義的繼承自WebSecurityConfigurerAdapter的類,通過查看父類的定義,我們知道調用build()方法最后返回的必須是一個Filter對象,可以自行參考頂級父類(或接口)WebSecurityConfigurer和SecurityBuilder
-
T2這里直接用new 來創建一個WebSecurity的對象 -
T3當有多個配置項時進行排序 -
T4進行order重復驗證 -
T5將配置信息配置到webSecurity中,變量configurers中會存儲這個信息
WebSecurityConfiguration中的 springSecurityFilterChain()方法
- 為我們創建了一個名字叫做
springSecurityFilterChain的Filter
源代碼:
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
//這里的意思是我們是否有自定義配置其實就是是否有注入WebSecurityConfigurer的子類,沒有的話,我默認的創建一個默認的,但是默認的我們自己不可修改
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
//請參考上面的代碼
webSecurity.apply(adapter);
}
//現在為止webSecurity對象的信息已經填充完畢,我們可以構建一個Filter
return webSecurity.build();
}
- 請查看代碼的注釋,我們可以知道,到此為止,已經建立了一個
Filter對象,而這個Filter將會攔截掉我們的請求,對請求進行過濾攔截,從而起到對資源進行認證保護的作用。然后這個Filter並非我們自己平時定義的Filter這么簡單,這個過濾器也只是一個代理的過濾器而已,里面還會有過濾器鏈,下一篇文章會針對過濾器鏈進行編寫。
2.2.3 WebSecurityConfigurerAdapter為我們做了什么
- 還是從最重要的開始
- 1.
HttpSecurity通過getHttp()獲取,后面會詳細說到這個類 -
-
UserDetailsService用戶信息獲取
-
-
-
AuthenticationManager認證管理類
后面會詳細講解到這些信息,包括這些信息在過濾其中起到什么作用,然后最重要的是,我們要先理清楚過濾器的機制,下一篇會詳細講過濾器鏈
-
