Spring Security原理篇(一) 啟動原理


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 使我們能根據業務擴展我們的應用,而WebSecurityConfigurerAdapterWebSecurityConfigurer 的一個適配器,必然也是做了很多默認的工作。后面我們會一一說到。

從以上可以稍微總結一下我們下一步需要探究的問題,

  • WebSecurityConfigurerAdapter到底為我們做了什么工作.
  • 上面注解的源代碼中Import了一個很重要的配置類WebSecurityConfiguration怎樣組件我們的過濾器(或者說過濾器鏈)

2.2.2 spring security創建流程

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&lt;Filter, WebSecurity&gt; 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> &amp;&amp; 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&lt;Filter, WebSecurity&gt; 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對象,可以自行參考頂級父類(或接口)WebSecurityConfigurerSecurityBuilder

  • 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()獲取,后面會詳細說到這個類
    1. UserDetailsService 用戶信息獲取
    1. AuthenticationManager 認證管理類
      后面會詳細講解到這些信息,包括這些信息在過濾其中起到什么作用,然后最重要的是,我們要先理清楚過濾器的機制,下一篇會詳細講過濾器鏈
原文地址:https://www.jianshu.com/p/0c54788c94f3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM