Spring Security(一) -- 初識Spring Security
作者: 屈定
博客: http://mrdear.cn/
Spring Security是什麼?
Spring Security是一套認證授權框架,支持認證模式如HTTP BASIC 認證頭 (基於 IETF RFC-based 標準),HTTP Digest 認證頭 ( IETF RFC-based 標準),Form-based authentication (用於簡單的用戶界面),OpenID 認證等,Spring Security使得當前系統可以快速集成這些驗證機制亦或是實現自己的一套驗證機制.
使用Spring Security
Spring Security3之後提供了Java Config的配置方式,但是我覺得xml方式比較容易理解其整體結構,所以本文都是基於xml配置的.
pom依賴
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
由於使用了Spring Boot,所以需要使用@EnableWebSecurity註解啟用Spring Security,並指明其配置文件為classpath下的spring-security.xml
@Configuration@EnableWebSecurity@ImportResource(locations = "classpath:spring-security.xml")public class SecurityConfig {}
xml配置
在spring-security.xml中引入官方提供的命名空間,然後簡單配置下,該配置大概意思是對所有請求的url攔截,必須有User許可權的用戶才能訪問.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <security:http > <security:intercept-url pattern="/**" access="hasRole(ROLE_USER)"/> <security:form-login/> <security:http-basic/> <security:logout/> </security:http> <security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="user" password="123456" authorities="ROLE_USER"/> <security:user name="admin" password="123456" authorities="ROLE_USER, ROLE_ADMIN"/> </security:user-service> </security:authentication-provider> </security:authentication-manager></beans>
訪問測試
該頁面為Spring Security自動生成的登錄頁面,當我們訪問任何連接都會被重定向到該登錄頁面,輸入user:123456登錄後才能有許可權訪問.
分析
上述是一個簡單的Demo,分析則是從這個Demo深入淺出.
1.Spring Security是如何攔截請求的?
傳統的xml配置都會在web.xml裡面配置如下過濾器.
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
可以看出入口點就是該類,該類會從Spring容器中讀取名稱為springSecurityFilterChain的一個Filter實例,從而獲取到對應代理的Filter.
protected Filter initDelegate(WebApplicationContext wac) throws ServletException { Filter delegate = wac.getBean(getTargetBeanName(), Filter.class); if (isTargetFilterLifecycle()) { delegate.init(getFilterConfig()); } return delegate; }
然後在doFilter方法中調用該委託的filter,也就實現的攔截請求.
protected void invokeDelegate( Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { delegate.doFilter(request, response, filterChain); }
2. Spring Security攔截請求後是如何處理的?
打斷點可以發現DelegatingFilterProxy實際上代理的是FilterChainProxy這個類,該類中有private List<SecurityFilterChain> filterChains;全局變數,那麼SecurityFilterChain為何物?
public interface SecurityFilterChain { boolean matches(HttpServletRequest request); List<Filter> getFilters();}
從源碼可以判斷SecurityFilterChain是一套規則所對應的Filter鏈集合.再看源碼getFilters,該方法會根據規則(也就是配置中的security:http標籤)獲取一個SecurityFilterChain中的一套對應規則的filter鏈.
private List<Filter> getFilters(HttpServletRequest request) { for (SecurityFilterChain chain : filterChains) { if (chain.matches(request)) { return chain.getFilters(); } } return null; }
最後在doFilterInternal方法中創建一個VirtualFilterChain類,調用其doFilter方法.VirtualFilterChain這個類很有意思,該類繼承了FilterChain類,那麼其就擁有了轉交請求到指定filter的能力,另外其還擁有一套filter鏈List<Filter> additionalFilters;,那麼這個類就控制了整個Spring Security的執行流程,那麼它是怎麼實現的呢?
開始我以為是一個循環,然而看了源碼才發現自己太low了.
currentPosition++; Filter nextFilter = additionalFilters.get(currentPosition - 1); nextFilter.doFilter(request, response, this);
currentPosition與additionalFilters都是全局變數,其在調用filter鏈的時候每次都把自己本身在doFilter傳值過去,每一個Filter鏈節點執行完畢後再返回VirtualFilterChain的doFilter方法,開啟下一個節點執行.其結構如下面代碼所示:
interface IA{ void doSomeThing(IAChain chain); } static class IAClass implements IA{ @Override public void doSomeThing(IAChain chain) { System.out.println("i am IAClass"); chain.doSomeThing(); } } interface IAChain{ void doSomeThing(); } static class IAChainClass implements IAChain{ List<IA> IAChains = new ArrayList<IA>(); public IAChainClass() { IAChains.add(new IAClass()); IAChains.add(new IAClass()); IAChains.add(new IAClass()); } int position = 0; @Override public void doSomeThing() { if (position == IAChains.size()) { System.out.println("end"); return; } IA ia = IAChains.get(position++); ia.doSomeThing(this); } }
當調用iaChainClass.doSomeThing()輸出
i am IAClassi am IAClassi am IAClassend
調用鏈的實現還可以使用繼承來實現,每次執行前先執行super()方法.
ok,下一章分析具體的Filter鏈中的節點,探究下Spring Security是如何進行用戶認證與許可權控制的.
推薦閱讀:
※史上最簡單的SpringCloud教程 | 第八篇: 消息匯流排(Spring Cloud Bus)
※Spring Boot中使用Flyway來管理資料庫版本
※Spring Security源碼分析五:Spring Security實現簡訊登錄
※用小說的形式講解Spring(1) —— 為什麼需要依賴注入
TAG:Spring |