spring security源碼分析之web包分析

Spring 是一個很是流行和成功的 Java 應用開發框架。Spring Security 基於 Spring 框架,提供了一套 Web 應用安全性的完整解決方案。通常來講,Web 應用的安全性包括用戶認證(Authentication)和用戶受權(Authorization)兩個部分。用戶認證指的是驗證某個用戶是否爲系統中的合法主體,也就是說用戶可否訪問該系統。用戶認證通常要求用戶提供用戶名和密碼。系統經過校驗用戶名和密碼來完成認證過程。用戶受權指的是驗證某個用戶是否有權限執行某個操做。在一個系統中,不一樣用戶所具備的權限是不一樣的。好比對一個文件來講,有的用戶只能進行讀取,而有的用戶能夠進行修改。通常來講,系統會爲不一樣的用戶分配不一樣的角色,而每一個角色則對應一系列的權限。java

對於上面提到的兩種應用情景,Spring Security 框架都有很好的支持。在用戶認證方面,Spring Security 框架支持主流的認證方式,包括 HTTP 基本認證、HTTP 表單驗證、HTTP 摘要認證、OpenID 和 LDAP 等。在用戶受權方面,Spring Security 提供了基於角色的訪問控制和訪問控制列表(Access Control List,ACL),能夠對應用中的領域對象進行細粒度的控制。linux

本文先從web包來分析spring-security提供的安全保護。web

1. access模塊:spring

其中,ExceptionTranslationFilter:處理過濾器鏈拋出的全部AccessDeniedException和AuthenticationException異常.chrome

WebInvocationPrivilegeEvaluator:容許用戶來決定他們是否有訪問特定web url的權限。express

   1.1 channel包:確保從指定傳輸通道接收web請求。apache

其中,api

ChannelProcessingFilter: 確保一個web請求經過要求的channel。在內部使用FilterInvocation來表示request請求,容許使用FilterInvoctionSecurityMetaDataSource來查詢應用之上的屬性。
代理ChannelDecisionManager來處理真實的通道安全Decision和必須的action。若響應由ChannelDecisionManager來提交,ChannelProcessingFilter將不會處理。

下面的示例強制將登錄表單和對/secure路徑下的訪問都經過https來訪問。瀏覽器

<bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
  <property name="channelDecisionManager" ref="channelDecisionManager"/>
  <property name="securityMetadataSource">
    <security:filter-security-metadata-source path-type="regex">
      <security:intercept-url pattern="\A/secure/.*\Z" access="REQUIRES_SECURE_CHANNEL"/>
      <security:intercept-url pattern="\A/login.jsp.*\Z" access="REQUIRES_SECURE_CHANNEL"/>
      <security:intercept-url pattern="\A/.*\Z" access="ANY_CHANNEL"/>
    </security:filter-security-metadata-source>
  </property>
</bean>

<bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
  <property name="channelProcessors">
    <list>
    <ref bean="secureChannelProcessor"/>
    <ref bean="insecureChannelProcessor"/>
    </list>
  </property>
</bean>

<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor"/>
channelDecisionManager:肯定一個web channel是否提供了足夠的安全性。
ChannelProcessor:肯定一個web channel是否知足特定安全條件。
ChannelEntryPoint:由ChannelProcessor使用來啓動一個web channel。

 1.2 expression包:安全

   

SecurityExpressionHandler 是一個門面,它將內在的表達式對象實現和spring security對安全表達式的需求分離開來。

DefaultWebSecurityExpressionHandler是SecurityExpressionHandler的默認實現。

    protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
        WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, fi);
        root.setPermissionEvaluator(getPermissionEvaluator());
        root.setTrustResolver(trustResolver);
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }

 1.3 intercept包:加強http請求的安全性,特別是url請求。

   

 

其中,FilterSecurityInterceptor 經過實現了filter來增長http資源的安全性。這個安全攔截器須要FilterInvocationSecurityMedataSource。

 

2. authentication模塊

認證處理機制,支持多種協議如BASIC,CAS,form login等提交認證信息。

    2.1 logout和rememberme 包

 其中

  LogoutFilter記錄用戶的退出,它包含了一組Logouthandler。這些hangler應用按照順序排序,順序是你想調用TockenBasedRememberMeServices和securityContxtLogoutHander的順序。退出後,將由LogoutSucessHandler或者LogoutSuccesUrl來決定跳轉到哪裏。到底由誰肯定依賴於建立LogoutFilter使用的構造方法。

  RememberMeAuthenticationFilter檢查SecurityContext中是否有Authentication對象,而且當有RememberMeServices實現了該請求時將一個remember-me authentication token設置到SecurityContext中。這個過濾器會調用RememberMeServices實現的autoLogin方法,若是上述方法返回一個非空的Authentication對象,會被傳遞到AuthenticationManager,這樣任何特定authentication將能夠完成。若Authentication結果返回成功,它將會被設置到SecurityContext中。

  若是認證成功,一個InteractiveAuthenticationSuccessEvent時間將會發布到application context中,若認證不成功,則不會有事件發佈,由於不成功的話會記錄成特定AuthenticationManager的應用事件。 正常狀況下,無論Authentication是成功仍是失敗,都會容許處理請求request。若是須要控制特定認證用戶的訪問目的,能夠將AuthenticationSuccessHandler注入其中。

  2.2 peauth包

  支持已認證的場景--spring 假定請求request已經被外部配置系統認證經過的場景。

AuthenticationDetailsSource:給特定的web請求request提供一個Authentication接口提供一個getDetails()方法

J2eePreAuthenticatedProcessingFilter:基於j2ee容器認證機制的過濾器,它將使用j2ee 用戶principal名稱做爲預先完成認證的principal。

WebSpherePreAuthenticatedProcessingFilter:基於Websphere認證的過濾器,它將使用Websphere RunAs 用戶principal名稱做爲先完成認證的principal。

X509AuthenticationFilter:負責處理要求未認證用戶提供客戶端證書的請求。若是這個請求包含了合法的證書,它將會使SubjectDnX509PrincipalExtractor抽取出安全實體.

RequestHeaderAuthenticationFilter:一個簡單的預先認證完成過濾器,它從用戶的請求頭獲取用戶名,使用在諸如CA siteminder系統等。

  2.3 session和switchuser

  session包:提供一個新認證用戶處理session相關行爲的策略接口和實現類

  switchuser包:提供一個基於http的具備切換用戶能力的包。相似於linux中的su命令。

 SessionAuthenticationStrategy:在一次認證過程當中對httpSession相關的行爲容許可插拔支持。典型應用場景是確認session是否存在或者改變session id來保證基於session攻擊的安全。

SwitchUserFilter:高權限用戶向低權限用戶切換 。

  2. 4 ui包

DefaultLoginPageGeneratingFilter:當一個用戶沒有配置login頁面時使用。僅當跳轉到login頁面時用到。

  2.5 www包

  BasicAuthenticationFilter:處理一個http請求的basic認證頭,將結果放入SecurityContextHolder。

總之,該過濾器負責處理具備basic認證scheme和base64編碼的username:password token的http請求頭消息.例如,認證一個名爲"Aladdin"的用戶,其密碼爲「open sesame」 ,其頭部以下:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

該過濾器不但能夠用來爲遠程協議客戶端(如hessian和soap)提供basic認證服務,還能夠爲標準的用戶代理(如ie和netscape)提供basic認證服務。若是認證成功,認證結果Authentication對象將會放入SecurityContextHolder之中。若是認證失敗而且<ignoreFailure>設置爲false(默認),將會調用AuthenticationEntryPoint實現類(除非<ignoreFailure>屬性被設置爲true)。一般狀況下是BasicAuthenticationEntryPoint,它提醒用戶經過Basic認證方式從新認證。因basic認證協議的簡單和普遍部署,它是一個很是有吸引力的協議。然而,因爲該協議經過明確的text傳遞密碼,所以它不適用於不少應用場景。spring security提供的Digest認證能夠在這些場景中替換Basic認證。

注意:若設置了RememberMeService,該filter將自動給用戶返回remember-me細節。

  DigestAuthenticationFilter:參照BasicAuthenticationFilter,注意:digest認證的缺點,儘管digest認證方式比basic認證方式更全面、更安全,但Rfc2617第四部分詳細討論了digest認證方式比basic認證方式的好處,也論述了digest的缺陷。

3.bind模塊

  AuthenticationPrincipal註解:綁定一個方法的參數或者方法到Authentication的getPrincipal()方法。必須指明,參數應該解析到當前用戶而不是能夠在表單編輯的用戶。示例以下:

 @Controller
public class MyController {
    @RequestMapping("/user/current/show")
     public String show(@AuthenticationPrincipal CustomUser customUser) {
         // do something with CustomUser
         return "view";
     }

AuthenticationPrincipalArgumentResolver:解析AuthenticationPrincipal註解。上述例子也能夠這樣作:

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@AuthenticationPrincipal
public @interface CurrentUser {
  }

  @Controller
  public class MyController {
      @RequestMapping("/user/current/show")
      public String show(@CurrentUser CustomUser customUser) {
          // do something with CustomUser
          return "view";
      }

4.上下文context模塊

同步SecurityContextPersistenceFilter:從配置的SecurityContextRepository而不是request中獲取信息存到SecurityContextHolder,而且當請求結束清理contextHolder時將值存回repository中(默認使用HttpSessionSecurityContextRepository).在該過濾器中每個請求僅執行一次,該filter需在任何認證處理機制其做用以前執行。認證處理機制如basic,cas等指望在執行時從SecurityContextHolder中獲取SecurityContext。

異步WebAsyncManagerIntegrationFilter:提供了對securityContext和WebAsyncManager的集成。方式是經過SecurityContextCallableProcessingInterceptor的beforeConcurrentHandling(NativeWebRequest, Callable)方法來說SecurityContext設置到Callable上。

SecurityContextCallableProcessingInterceptor:支持spring mvc Callable集成。當SecurityContextCallableProcessingInterceptor執行preProcess(NativeWebRequest, Callable)方法時將注入的SecurityContext傳遞給SecurityContextHolder。

    @Override
    public <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception {
        SecurityContextHolder.setContext(securityContext);
    }

一樣清楚的是,SecurityContextCallableProcessingInterceptor在執行postProcess(NativeWebRequest, Callable, Object)方法時調用SecurityContextHolder的clearContext()。

    @Override
    public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) throws Exception {
        SecurityContextHolder.clearContext();
    }

5 csrf模塊

Cross-site request forgery跨站請求僞造,也被稱爲「one click attack」或者session riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。

CsrfException:當一個HttpServletRequest沒有有效的CsrfToken或者沒有CsrfToken時拋出的異常,有兩個子類:InvalidCsrfTokenException和MissingCsrfTokenException。

CsrfToken提供了一個指望的csrf token信息。默認實現爲DefaultCsrfToken,還有一個內部實現類SaveOnAccessCsrfToken。

CsrfTokenRepository:將crsfToken與HttpServletRequest關聯起來,使之能關聯CsrfToken的api。例如,能夠存儲到HttpSesssion中。默認實現爲HttpSessionCsrfTokenRepository。

 CsrfFilter:經過使用同步token模式來進行csrf防禦。

CsrfLogoutHandler 負責在退出時移除csrfToken。調用時在LogoutFilter中的logout方法中:

            for (LogoutHandler handler : handlers) {
                handler.logout(request, response, auth);
            }

6. Debug模塊

  DebugFilter:spring security的調試過濾器。爲幫助用戶理解請求request是如何被spring security處理的,使用日誌記錄諸如session建立等消息。同時也會記錄一些別的相關消息。

  Logger封裝了apache commons-logging。

7.firewall模塊

  HttpFirewall接口時爲了阻止潛在威脅的請求而將請求進行封裝來控制這些請求的行爲的接口。

 DefaultHttpFirewall是默認實現。主要是檢查一個路徑是否合法:

 /**
     * Checks whether a path is normalized (doesn't contain path traversal sequences like "./", "/../" or "/.")
     *
     * @param path the path to test
     * @return true if the path doesn't contain any path-traversal character sequences.
     */
    private boolean isNormalized(String path) {
        if (path == null) {
            return true;
        }

        for (int j = path.length(); j > 0;) {
            int i = path.lastIndexOf('/', j - 1);
            int gap = j - i;

            if (gap == 2 && path.charAt(i+1) == '.') {
                // ".", "/./" or "/."
                return false;
            } else if (gap == 3 && path.charAt(i+1) == '.'&& path.charAt(i+2) == '.') {
                return false;
            }

            j = i;
        }

        return true;
    }

FirewalledRequest是個抽象類,是請求request的封裝,返回一個HttpFirewall接口。不一樣之處在於reset方法,該方法容許當請求離開security filter chain時重置部分或者所有狀態。默認實現爲RequestWrapper。

8. header模塊

HeaderWriter是一個向HttpServletResponse寫入http請求頭的約定。

HeaderWriterFilter向當前請求中增長http請求頭的過濾器,爲瀏覽器保護增長特定的http請求頭。如X-FRAME-options(X-Frame-Options response header 可用於指示是否應該容許瀏覽器呈如今一個頁面<FRAME> 或 <IFRAME>中. 以確保網站內容是否是嵌入到其它網站. )、x-xss-protection(在正常狀況下,經過下面的HTTP header,就能夠完美的關閉發送這個header的頁面XSS保護特性了。X-XSS-Protection: 0)和x-content-type-options(這個header主要用來防止在IE九、chrome和safari中的MIME類型混淆攻擊。firefox目前對此還存在爭議。一般瀏覽器能夠經過嗅探內容自己的方法來決定它是什麼類型,而不是看響應中的content-type值。經過設置 X-Content-Type-Options:若是content-type和指望的類型匹配,則不須要嗅探,只能從外部加載肯定類型的資源。)。

9.session模塊

ConcurrentSessionFilter:這個filter有兩個功能。第一:它調用SessionRegistry的refreshLastRequest(String)方法來保證註冊的session一般擁有正確的最後一次更新時間。

第二:它從每一個請求的SessionRegistry中檢索SessionInformation,並檢查session是否已經標示爲過時。若標示爲過時,則調用配置的全部logout handler(在LogoutFilter中調用),一般的場景是使session過時。此時會跳轉到指定的expiredURL,session過時會經過註冊在<web.xml>的HttpSessionEventPublisher產生一個HttpSessionDestroyedEvent事件。

10 小結

   Spring Security對Web安全性的支持大量地依賴於Servlet過濾器。這些過濾器攔截進入請求,而且在應用程序處理該請求以前進行某些安全處理。 Spring Security提供有若干個過濾器,它們可以攔截Servlet請求,並將這些請求轉給認證和訪問決策管理器處理,從而加強安全性。

參考文獻:

1. http://www.ibm.com/developerworks/cn/java/j-lo-springsecurity/

2. http://baike.baidu.com/link?url=mk6ZedYayBqvXehW094XdImcU9g2SGLgt-gmdKYAF17db97_mEloPPm3K-1eqqEymefNNO30b2CTGw2Ryf6amq

相關文章
相關標籤/搜索