天天看點

基于SAML協定 實作SP 單點登入基于SAML協定及SpringSecurity 實作單點登入 SAML SP端

基于SAML協定及SpringSecurity 實作單點登入 SAML SP端

一. 什麼是SAML協定

首先還是簡單介紹一下什麼是SAML協定:

SAML 即安全斷言标記語言,英文全稱是 Security Assertion Markup Language。它是一個基于 XML 的标準,用于在不同的安全域(security domain)之間交換認證和授權資料。在 SAML 标準定義了身份提供者 (identity provider) 和服務提供者 (service provider),這兩者構成了前面所說的不同的安全域。 SAML 是 OASIS 組織安全服務技術委員會(Security Services Technical Committee) 的産品。

SAML(Security Assertion Markup Language)是一個 XML 架構,也就是一組協定,可以用來傳輸安全聲明。比如,兩台遠端機器之間要通訊,為了保證安全,我們可以采用加密等措施,也可以采用 SAML 來傳輸,傳輸的資料以 XML 形式,符合 SAML 規範,這樣我們就可以不要求兩台機器采用什麼樣的系統,隻要求能了解 SAML 規範即可,顯然比傳統的方式更好。SAML 規範是一組 Schema 定義。

可以這麼說,在 Web Service 領域,schema 就是規範,在 Java 領域,API 就是規範。

沒用過SpringSecurity 的點這裡這裡有官方文檔

二. SAML的作用及相關定義

1. 作用

認證聲明:聲明使用者是否已經認證,通常用于單點登入。

屬性聲明:聲明某個 Subject 所具有的屬性。

授權決策聲明:聲明某個資源的權限,即一個使用者在資源 R 上具有給定的 E權限而能夠執行 A 操作。

想要更深刻的了解SAML協定的 ,看這裡,這裡附上(淺夏、未央)有關于SAML協定的詳細講解(有一、二、三章節)

2. 組成

SP(Service Provider): 向使用者提供正式商業服務的實體,通常需要認證一個使用者的身份;

IDP(Identity Provider): 提供使用者的身份鑒别,確定使用者是其所聲明的身份

斷言 (Assertions) 即資訊:斷言是在 SAML 中用來描述認證的對象,包括使用者在什麼時間、以什麼方式被認證,還可以包括一些擴充資訊,比如使用者的 Email 位址和電話。

協定 (Protocol) 即通信:協定規定如何執行不同的行為。這些行為被細化成一些列的 Request 和 Response 對象,而在這些請求和相應的對象中包含了行為所特别需要的資訊。比如,認證請求協定(AuthnRequest Protocol)就規定了一個 SP 如何請求去獲得一個被認證的與使用者。

綁定 (Binding) 即傳輸:綁定定義了 SAML 資訊如何使用通信協定被傳輸的。比如,HTTP 重定向綁定,即聲明 SAML 資訊将通過 HTTP 重定向消息傳輸;再比如 SAML SOAP 綁定,聲明了通過 SOAP 來傳遞 SAML 消息。比如上面 AuthnRequest 就聲明了 Http-POst 綁定

中繼資料 (MetaData):SAML 的中繼資料是配置資料,其包含關于 SAML 通信各方的資訊,比如通信另一方的 ID、Web Service 的 IP 位址、所支援的綁定類型以及通信中實用的密鑰等等。

三. SAML流程分析

基于SAML協定 實作SP 單點登入基于SAML協定及SpringSecurity 實作單點登入 SAML SP端

此圖檔說明了以下步驟。

1.使用者嘗試通路WebApp1。

2.WebApp1 生成一個 SAML 身份驗證請求。SAML 請求将進行編碼并嵌入到SSO 服務的網址中。包含使用者嘗試通路的 WebApp1 應用程式的編碼網址的 RelayState 參數也會嵌入到 SSO 網址中。該 RelayState 參數作為不透明辨別符,将直接傳回該辨別符而不進行任何修改或檢查。

3.WebApp1将重定向發送到使用者的浏覽器。重定向網址包含應向SSO 服務送出的編碼 SAML 身份驗證請求。

4.SSO(統一認證中心或叫Identity Provider)解碼 SAML 請求,并提取 WebApp1的 ACS(聲明客戶服務)網址以及使用者的目标網址(RelayState 參數)。然後,統一認證中心對使用者進行身份驗證。統一認證中心可能會要求提供有效登入憑據或檢查有效會話 Cookie 以驗證使用者身份。

5.統一認證中心生成一個 SAML 響應,其中包含經過驗證的使用者的使用者名。按照 SAML 2.0 規範,此響應将使用統一認證中心的 DSA/RSA 公鑰和私鑰進行數字簽名。

6.統一認證中心對 SAML 響應和 RelayState 參數進行編碼,并将該資訊傳回到使用者的浏覽器。統一認證中心提供了一種機制,以便浏覽器可以将該資訊轉發到 WebApp1 ACS。

7.WebApp1使用統一認證中心的公鑰驗證 SAML 響應。如果成功驗證該響應,ACS 則會将使用者重定向到目标網址。

8. 使用者将重定向到目标網址并登入到 WebApp1。

四.Git中的開源項目

1.完整的SAML SP及IDP實作通信的簡單Demo位址

https://github.com/OpenConext/Mujina
           

2.基于Spring Security實作的SAML SP(注意這裡隻有SP) 項目位址

https://github.com/vdenotaris/spring-boot-security-saml-sample.git
           

3.這裡是我自己基于上一條實作的配置版SP的Demo,小白代碼比較辣雞歡迎大神指點

https://github.com/jwu09188/saml-sso-sp-spring-security
           

這個項目是做成一個簡單的SP 架構 内部不具備啟動類 想要使用 隻能建立項目實作外部作為jar包引用,注意(自己的啟動類上記得加上bean掃描注解掃描)需要自己配置yml,列子如下:

server:
  port: 9090
logging:
  file: logs/file.log
  level:
    com:
      jiujin:
        web:
          saml: DEBUG
    org:
      opensaml: DEBUG
      springframework:
        security:
          saml: DEBUG
sso:
  saml:
    sp:
      # Entity ID of the SP  
      sp_entity_id: com:saml:spring:sp
      # Certificate
      sp_Certificate: MIIEzTCCAzWgAwIBAgIUdsfVXEJJbdL+K7FmAiBlXgLEg/owDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NJQ0hVQU4xEDAOBgNVBAcMB0NIRU5HRFUxDzANBgNVBAoMBkpJVUpJTjELMAkGA1UECwwCUkQxEDAOBgNVBAMMB0NHV1lDT00xEzARBgkqhkiG9w0BCQEWBFVVVVUwHhcNMjAwNzE1MDgyMTU3WhcNMzAwNzE1MDgyMTU3WjB2MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHU0lDSFVBTjEQMA4GA1UEBwwHQ0hFTkdEVTEPMA0GA1UECgwGSklVSklOMQswCQYDVQQLDAJSRDEQMA4GA1UEAwwHQ0dXWUNPTTETMBEGCSqGSIb3DQEJARYEVVVVVTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANWpWb41MrSLIWsdelCYRxySClXcvEF6l69f+EX877xY89EVH48xnckH6u4IciFXGaAavRR2RLOe4uBf+RU8Kk9+EuJL5tFVYOo4Wf0p4c6Bf62IhiUOpDKPTaaGCnDBR2ZiC6enpMYbMHxJ/HSbWCoBodH81le7NBekE0YNtGLaesX9so5mYbVURnKhpM5oX0OAo20wvZBomK76Zi6pEj9zJzzbCahWtkkutJLf2Z8DQHweEEouKzoo3USNpD7/VdRjvMw/mJwAIPupipJvQH7s2jMkt/CDFdd7SQr+CxccVPC1phkBQygwDK4SZok0qhQv4FFpSKWUVQTUPMw37cPG2dZ+Up4bPYp48tmDp1qP1PPpbmNGUBqrwR6B9vQ635xrr2By7i9PlkzUz2BQ3hvlXZ39aW4cmzRomN3AldWuoj7nzEdyeJJI5/8zs7ETqInnVqrEjKwU4bkSZu6hZR5+YKgr1KZ37itXryyXxv324g1QLZ7FMaXtlNmTRIF7BwIDAQABo1MwUTAdBgNVHQ4EFgQUQQzvKnZNr/6tZaDBEjcoCN5fiuYwHwYDVR0jBBgwFoAUQQzvKnZNr/6tZaDBEjcoCN5fiuYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAYEAYwrZZ+OgsVXVJlYVHHVrOLXOvaee/c3DFarpwu1VW3hsRWOToGrSMybly0atY5pAYT/rka6rDHW/8W1RB0uaDLI8HYcwyH/7VQvq3uqO8QO/QAbXUAQYjAQSexIu0DV2JFw/SQN4KOt5j/A4gL4Cw4bg0ZosHbH40ZHgeo49MLvyYDzXk1SbwFVO1HonAlEtElcrwcvYLNiqr7Arvb9tb8pmDQIR94LwfNEc4jikcuAOH5Lh/G3tiaewQdkxeUPG+HXgwR8OV51dVTOkOhJVQeioE1ZUKniIcuukCDY/vFBihYCy+Pfver0C39ECllu9vfYuSogfBQ09c5HPkDG0Q428RHDHQbKLP9K3pd6Vw3x2a5Yj3SgcDQ19I5IChS6WmIlU4pTfljWc1kfc+wQ/Fp7jXXzEz9gOVPtf9840SJX3119g9d5T19mH8mj5MbeiY10BOAJzZXQNeWcNBEJu7yNQ6/gBoCaZXAYI6xLZk7oFmZNGpjmCbw9BhtbJ6WDZ
      # Passphrase of the keystore
      sp_passphrase: secret
      # private key
      sp_private_key: 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
      # Resource URL for the idp metadata idp  
      idp_metadata_url: idp的metadata位址  
      # IDP Discovery Service fill in what you need to be found controller
      idp_discovery_service: /saml-sso/sp/login-redirect
      # jump address after successful login url
      redirection_after_successful_url: https://www.baidu.com
           

注意!!配置檔案中的 idp_discovery_service: /saml-sso/sp/login-redirect發現服務這裡配置的是 你需要被idp 發現的 自己定義的controller

私鑰和證書的生成方式不會的小夥伴可以點選這裡有(拖鞋公子)的生成教程

字首由你自己定義的本機端口示例:

localhost:9090/saml-sso/sp/metadata
           
SP metadata擷取位址: .../saml-sso/sp/metadata
登出位址: .../saml-sso/sp/logout-redirect
登入位址: .../saml-sso/sp/login
           

以上位址皆可在 WebSecurityConfig 類 samlFilter() 方法中自行修改為自己需要的,注意:方法中傳入的 samlEntryPoint()方法 你自己需要點進去看有沒有設定的有位址 有的話也需要修改

/**
     * 定義安全篩選器鍊,以便通過使用SAML 2.0支援SSO身份驗證
     *
     * @return Filter chain proxy
     * @throws Exception
     */
    @Bean
    public FilterChainProxy samlFilter() throws Exception {
        List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/login/**"),
                samlEntryPoint()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/logout-redirect/**"),
                samlLogoutFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/metadata/**"),
                metadataDisplayFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/assert"),
                samlWebSSOProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/single-logout/**"),
                samlLogoutProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/login-redirect/**"),
                samlIDPDiscovery()));
        return new FilterChainProxy(chains);
    }
           

指定登出成功後自己需要跳轉位址修改 successLogoutHandler() 方法,設定下文中 successLogoutHandler.setDefaultTargetUrl(); 設定自己需要的位址

@Bean
    public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
        SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
        successLogoutHandler.setDefaultTargetUrl("/saml-sso/sp/login-redirect");
        return successLogoutHandler;
           

還有最後一點需要注意的就是

項目中的其他方法就不全部介紹了項目裡面都有注釋

這裡是本人的第一篇部落格隻是粗略研究了基于SAML的單點登入應用,認知有限,不對之處請各位前輩指點。同時借此博文分享我的學習心得,抛磚引玉。