<div id="cnblogs_post_body" class="blogpost-body cnblogs-markdown"><div class="show-content-free"> <h1>1.概述</h1> <ul> <li> <code>spring security</code>有參考的中文翻譯文檔<a href="https://springcloud.cc/spring-security-zhcn.html" target="_blank" rel="nofollow">https://springcloud.cc/spring-security-zhcn.html</a> </li> <li>在學習<code>spring security</code>的時候,參考的書籍並非不少,線上的技術博文也是沒有系統的全面的介紹,不得不去參考源代碼,發費了大量時間和精力,而我試圖也經過一系列的文章,能結合本身的學習經歷,按部就班的表達清楚本身所理解到的點。</li> <li>全部的文章都是對源代碼的解讀,最後會給出一個小小的demo。</li> <li>選用的 <code>spring boot</code>版本<code>1.4.3.RELEASE</code>,<code>spring cloud</code>版本<code>Camden.SR4</code>(後面有結合網關zuul作oauth2 認證受權介紹)。版本非spring 最新版本,選用的是平時使用最多版本。</li> <li>文章是做爲學習筆記性質爲目的.固然若是文章能幫到有須要的朋友天然是特別開心和幸福的事情。有理解錯誤和不到位的點也但願各位指出,一塊兒學習一塊兒成長,一直沒有都沒敢寫公開技術文章,第一本人水平有限,第二是不但願由於本身的錯誤給別人形成誤導。</li> </ul> <h1>2.啓動</h1> <h2>2.1 簡單的開始</h2> <p>從文檔中可知,spring引入咱們的項目只須要分爲兩步</p> <ul> <li>maven引入須要的包,spring boot中引入的spring security版本爲4.1.4</li> </ul> <pre class="hljs xml"><code class="xml hljs"> <span class="hljs-tag"><span class="hljs-tag"><</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">dependency</span></span></span><span class="hljs-tag">></span></span> <span class="hljs-tag"><span class="hljs-tag"><</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">groupId</span></span></span><span class="hljs-tag">></span></span>org.springframework.boot<span class="hljs-tag"><span class="hljs-tag"></</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">groupId</span></span></span><span class="hljs-tag">></span></span> <span class="hljs-tag"><span class="hljs-tag"><</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">artifactId</span></span></span><span class="hljs-tag">></span></span>spring-boot-starter-security<span class="hljs-tag"><span class="hljs-tag"></</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">artifactId</span></span></span><span class="hljs-tag">></span></span> <span class="hljs-tag"><span class="hljs-tag"></</span><span class="hljs-name"><span class="hljs-tag"><span class="hljs-name">dependency</span></span></span><span class="hljs-tag">></span></span> </code></pre> <ul> <li>配置spring security(spring security官方文檔給出的例子,後面簡稱例子1)</li> </ul> <pre class="hljs java"><code class="java hljs"><span class="hljs-meta"><span class="hljs-meta">@EnableWebSecurity</span></span> <span class="hljs-keyword"><span class="hljs-keyword">public</span></span> <span class="hljs-class"><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">class</span></span></span><span class="hljs-class"> </span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">WebSecurityConfig</span></span></span><span class="hljs-class"> </span><span class="hljs-keyword"><span class="hljs-class"><span class="hljs-keyword">extends</span></span></span><span class="hljs-class"> </span><span class="hljs-title"><span class="hljs-class"><span class="hljs-title">WebSecurityConfigurerAdapter</span></span></span><span class="hljs-class"> </span></span>{javascript
<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>); }
} </code></pre>css
<h2>2.2 簡單後面的繁瑣</h2> <p>咱們潔簡的應用spring security到咱們的項目,而後卻開啓了很是強大的功能,上面的例子,在內存中配置了一個用戶名爲user,密碼爲password,而且擁有USER角色的用戶。咱們仍是要探究一下,spring security到底爲咱們作了一些什麼樣的工做。<br> 咱們的入手點只有兩個,<code>@EnableWebSecurity</code>註解和<code>WebSecurityConfigurerAdapter</code>這個適配器類。</p> <h3>2.2.1 @EnableWebSecurity 註解</h3> <p>註解源代碼</p> <pre class="hljs css"><code class="css hljs">@<span class="hljs-keyword"><span class="hljs-keyword">Retention</span></span>(<span class="hljs-keyword"><span class="hljs-keyword">value</span></span> = java.lang.annotation.RetentionPolicy.RUNTIME) @Target(value = { <span class="hljs-selector-tag"><span class="hljs-selector-tag">java</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.lang</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.annotation</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.ElementType</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.TYPE</span></span> }) @<span class="hljs-keyword"><span class="hljs-keyword">Documented</span></span> @Import({ <span class="hljs-selector-tag"><span class="hljs-selector-tag">WebSecurityConfiguration</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.class</span></span>, <span class="hljs-selector-tag"><span class="hljs-selector-tag">SpringWebMvcImportSelector</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.class</span></span>, <span class="hljs-selector-tag"><span class="hljs-selector-tag">OAuth2ImportSelector</span></span><span class="hljs-selector-class"><span class="hljs-selector-class">.class</span></span> }) @<span class="hljs-keyword"><span class="hljs-keyword">EnableGlobalAuthentication</span></span> @Configuration public @interface EnableWebSecurity { <span class="hljs-selector-tag"><span class="hljs-selector-tag">boolean</span></span> <span class="hljs-selector-tag"><span class="hljs-selector-tag">debug</span></span>() <span class="hljs-selector-tag"><span class="hljs-selector-tag">default</span></span> <span class="hljs-selector-tag"><span class="hljs-selector-tag">false</span></span>; </code></pre> <p>此處仍是有必要簡單翻譯一下官方對於類的註釋,以便於更加清楚的理解註解的做用<br> 註釋中提到兩個很重要的點,以及一個配置示例,</p> <ul> <li>第一個點:<code>@EnableWebSecurity</code>配置到擁有註解 <code>@Configuration</code>的類上,就能夠獲取到spring security的支持.</li> <li>第二個點: <code>WebSecurityConfigurer</code>的子類能夠擴展spring security的應用</li> </ul> <p>由此可知<code>@EnableWebSecurity</code>使咱們擁有spring security的能力。<code>WebSecurityConfigurer</code> 使咱們能根據業務擴展咱們的應用,而<code>WebSecurityConfigurerAdapter</code>是<code>WebSecurityConfigurer</code> 的一個適配器,必然也是作了不少默認的工做。後面咱們會一一說到。</p> <p>從以上能夠稍微總結一下咱們下一步須要探究的問題,</p> <ul> <li> <code>WebSecurityConfigurerAdapter</code>到底爲咱們作了什麼工做.</li> <li>上面註解的源代碼中Import了一個很重要的配置類<code>WebSecurityConfiguration</code>怎樣組件咱們的過濾器(或者說過濾器鏈)</li> </ul> <h2>2.2.2 spring security建立流程</h2> <div class="image-package"> <div class="image-container" style="max-width: 507px; max-height: 545px; background-color: transparent;">html
<div class="image-view" data-width="507" data-height="545"><img data-original-src="//upload-images.jianshu.io/upload_images/3677408-5f5de55846dbc9dd.png" data-original-width="507" data-original-height="545" data-original-format="image/png" data-original-filesize="35764" class="" style="cursor: zoom-in;" src="https://img2018.cnblogs.com/blog/1112483/201906/1112483-20190614104107048-1838982520.png"></div> </div> <div class="image-caption">spring security加載流程</div> </div> <ul> <li>若是咱們忽略掉細節,只看最重要的步驟,大概如此.下面咱們對每個步驟來作相應的源代碼解釋</li> </ul> <h3> <code>WebSecurityConfiguration</code>中的 <code>setFilterChainProxySecurityConfigurer()</code>方法</h3> <pre class="hljs java"><code class="java hljs"><span class="hljs-meta"><span class="hljs-meta">@Autowired</span></span>(required = <span class="hljs-keyword"><span class="hljs-keyword">false</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">setFilterChainProxySecurityConfigurer</span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">( ObjectPostProcessor<Object> objectPostProcessor, //T1 使用@Value獲取到配置信息 @Value(</span></span><span class="hljs-string"><span class="hljs-function"><span class="hljs-params"><span class="hljs-string">"#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}"</span></span></span></span><span class="hljs-function"><span class="hljs-params">)</span></span></span><span class="hljs-function"> List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) </span><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">throws</span></span></span><span class="hljs-function"> Exception </span></span>{java
<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; }
</code></pre>web
<ul> <li> <code>T1</code>(註釋標記處)獲取配置信息</li> </ul> <pre class="hljs ruby"><code class="ruby hljs">@Value(<span class="hljs-string"><span class="hljs-string">"</span><span class="hljs-subst"><span class="hljs-string"><span class="hljs-subst">#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}</span></span></span><span class="hljs-string">"</span></span> </code></pre> <p>能夠看一下autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()的源代碼</p> <pre class="hljs javascript"><code class="javascript hljs">public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() { List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> ArrayList<SecurityConfigurer<Filter, WebSecurity>>(); <span class="hljs-built_in"><span class="hljs-built_in">Map</span></span><<span class="hljs-built_in"><span class="hljs-built_in">String</span></span>, WebSecurityConfigurer> beansOfType = beanFactory .getBeansOfType(WebSecurityConfigurer.class); <span class="hljs-keyword"><span class="hljs-keyword">for</span></span> (Entry<<span class="hljs-built_in"><span class="hljs-built_in">String</span></span>, WebSecurityConfigurer> entry : beansOfType.entrySet()) { webSecurityConfigurers.add(entry.getValue()); } <span class="hljs-keyword"><span class="hljs-keyword">return</span></span> webSecurityConfigurers; } </code></pre> <p>其實咱們根據debug獲取到的來看,這個beansOfType 就是咱們定義的繼承自WebSecurityConfigurerAdapter的類,經過查看父類的定義,咱們知道調用build()方法最後返回的必須是一個Filter對象,能夠自行參考頂級父類(或接口)<code>WebSecurityConfigurer</code>和<code>SecurityBuilder</code></p> <ul> <li> <code>T2</code>這裏直接用new 來建立一個WebSecurity的對象</li> <li> <code>T3</code>當有多個配置項時進行排序</li> <li> <code>T4</code>進行order重複驗證</li> <li> <code>T5</code>將配置信息配置到webSecurity中,變量configurers中會存儲這個信息</li> </ul> <h3> <code>WebSecurityConfiguration</code>中的 <code>springSecurityFilterChain()</code>方法</h3> <ul> <li>爲咱們建立了一個名字叫作<code>springSecurityFilterChain</code>的Filter<br> 源代碼:</li> </ul> <pre class="hljs java"><code class="java hljs"><span class="hljs-meta"><span class="hljs-meta">@Bean</span></span>(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) <span class="hljs-function"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword">public</span></span></span><span class="hljs-function"> Filter </span><span class="hljs-title"><span class="hljs-function"><span class="hljs-title">springSecurityFilterChain</span></span></span><span class="hljs-params"><span class="hljs-function"><span class="hljs-params">()</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>{ <span class="hljs-keyword"><span class="hljs-keyword">boolean</span></span> hasConfigurers = webSecurityConfigurers != <span class="hljs-keyword"><span class="hljs-keyword">null</span></span> && !webSecurityConfigurers.isEmpty(); <span class="hljs-comment"><span class="hljs-comment">//這裏的意思是咱們是否有自定義配置其實就是是否有注入WebSecurityConfigurer的子類,沒有的話,我默認的建立一個默認的,可是默認的咱們本身不可修改</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (!hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(<span class="hljs-keyword"><span class="hljs-keyword">new</span></span> WebSecurityConfigurerAdapter() { }); <span class="hljs-comment"><span class="hljs-comment">//請參考上面的代碼</span></span> webSecurity.apply(adapter); } <span class="hljs-comment"><span class="hljs-comment">//如今爲止webSecurity對象的信息已經填充完畢,咱們能夠構建一個Filter</span></span> <span class="hljs-keyword"><span class="hljs-keyword">return</span></span> webSecurity.build(); } </code></pre> <ul> <li>請查看代碼的註釋,咱們能夠知道,到此爲止,已經創建了一個<code>Filter</code>對象,而這個<code>Filter</code>將會攔截掉咱們的請求,對請求進行過濾攔截,從而起到對資源進行認證保護的做用。而後這個<code>Filter</code>並不是咱們本身平時定義的<code>Filter</code>這麼簡單,這個過濾器也只是一個代理的過濾器而已,裏面還會有過濾器鏈,下一篇文章會針對過濾器鏈進行編寫。</li> </ul> <h2>2.2.3 <code>WebSecurityConfigurerAdapter</code>爲咱們作了什麼</h2> <ul> <li>仍是從最重要的開始</li> <li>1.<code>HttpSecurity</code> 經過getHttp()獲取,後面會詳細說到這個類</li> <li><ol start="2"> <li> <code>UserDetailsService</code> 用戶信息獲取</li> </ol></li> <li><ol start="3"> <li> <code>AuthenticationManager</code> 認證管理類<br> 後面會詳細講解到這些信息,包括這些信息在過濾其中起到什麼做用,而後最重要的是,咱們要先理清楚過濾器的機制,下一篇會詳細講過濾器鏈</li> </ol></li> </ul> 原文地址:https://www.jianshu.com/p/0c54788c94f3 </div> </div>spring