過去三天,我們一直在討論「單一系統」要怎麼驗證使用者身份、怎麼維持登入狀態——從最基礎的 Basic Auth、Digest Auth、API Key、Session-based 登入,到另一個陣營的 Bearer Token、JWT、Access Token 與 Refresh Token。這些機制解決的都是同一個系統內部的問題。
但現實中,一個使用者要面對的往往不是一個系統,而是好幾十個系統,例如公司內部可能同時用 Gmail、Google Drive、Slack……如果每個系統都要求使用者各自登入一次,不但使用者要記住一大堆密碼,IT 團隊也要在每個系統各自管理帳號、各自處理忘記密碼的請求。這就是今天要談的 SSO(Single Sign-On,單一登入) 要解決的問題。
今天內容涵蓋:
沒有 SSO 的時候,使用者面對的是這樣的情境:每換一個系統就要重新輸入一次帳密。為了記得住,很多人會做兩件不安全的事——把密碼設得很簡單,或者好幾個系統共用同一組密碼。只要其中一個系統的密碼資料庫被攻破,攻擊者就能拿同一組帳密去嘗試登入其他系統。
而 IT 團隊要面對的,是另一種困擾:使用者一多,忘記密碼、重設密碼的請求就會變多,每個系統都要各自維護使用者帳號,也要處理權限的開通與收回;員工離職時則得一個系統接一個系統手動停用帳號,一旦漏了哪個系統沒做,就會留下資安風險。
SSO 想做的事很單純:讓使用者只登入一次,就能存取多個彼此獨立、互相信任的應用程式或系統,不用每個系統都重新輸入一次帳密。
這裡先定位一下 SSO 本身是什麼:嚴格說起來,SSO 只是一種使用者體驗模式(UX pattern),描述的是「登入一次、多處通行」這個結果,本身不是一種驗證方法;實際負責驗證身份的,是後面會介紹的 SAML、OIDC 這類身份協定。
簡單說,SSO 可以看成 IAM 裡的一個功能。SSO 解決的是「使用者不用每個系統都登入一次」的問題;IAM 管的範圍更大,還包含帳號怎麼建立、離職時怎麼停用、權限怎麼分配、誰能存取哪些資源,以及這些行為要怎麼被稽核。
也因為這樣,SSO 的價值不只是讓使用者少輸入幾次密碼,而是讓組織可以把登入政策集中管理。例如:要不要強制 MFA、密碼規則怎麼設計、員工離職時要不要停用帳號、哪些系統可以被哪些人存取,這些都可以集中在 IdP 或 IAM 系統裡處理,而不是散落在每一個應用程式裡。
SSO 要運作,需要先在角色之間建立信任關係,主要有兩個角色:

SP 之所以敢把驗證這件事交給 IdP,是因為兩者事先建立了信任關係。以 SAML 為例,IdP 和 SP 通常會交換 metadata,裡面包含 Entity ID、憑證/公鑰、登入與回傳用的 endpoint URL 等資訊。SP 之後收到 IdP 傳來的 Assertion 時,就可以用事先信任的公鑰驗證簽章,確認這份結果真的是由可信任的 IdP 發出,而且沒有被中途竄改。
以「使用者登入一次,接著依序存取應用系統 A、B、C」為例:

流程如下:
這裡要特別釐清「全域 Session」和 IdP 的關係。所謂全域 Session,指的是 IdP 自己維護的登入狀態。使用者第一次在 IdP 登入成功後,IdP 會透過 Session / Cookie 記住「這個使用者已經登入過」。之後其他 SP 把使用者導回 IdP 時,IdP 就可以根據這份登入狀態,判斷是否需要再次要求使用者輸入帳密。
但這不代表所有 SP 都共用同一份 Session。每個 SP 收到 IdP 回傳的驗證結果後,通常還是會在自己的系統裡建立一份本地 Session,或簽發自己的 Token。之後使用者操作這個 SP 時,就靠 SP 自己的登入狀態維持使用者身份。
所以 SSO 的重點不是「所有系統共用同一份 Session」,而是「所有 SP 都信任同一個 IdP」。IdP 負責確認使用者是否已經登入;SP 則根據 IdP 回傳的驗證結果,決定是否讓使用者進入自己的系統。至於 IdP 回傳給 SP 的「驗證結果」具體長什麼樣子,就是下一節要介紹的兩種常見協定的差異所在。
IdP 驗證完使用者身份之後,要用什麼格式把「這個人是誰、驗證通過了」這件事告訴 SP,目前最常見的兩套就是 SAML 與 OIDC。簡單先定位一下這兩套到底是什麼:SAML 是一套用 XML 描述「驗證聲明」的開放標準協定,歷史較久;OIDC 則是建立在 OAuth 2.0 這套授權框架之上、專門處理身份驗證的協定。
不過這篇會把重點放在 SSO 的整體概念與 SAML 的基本流程,OIDC 只先放在比較表裡定位,後面等 OAuth 2.0 講完後會再獨立開一篇細講。
兩套本質上都是在定義「驗證完之後,要用什麼格式跟 SP 溝通」,以存取 Salesforce(用 SAML)與 Gmail(用 OIDC)為例:

流程如下:
存取 Salesforce 這類用 SAML 整合的應用程式,走的是步驟 1~4;存取 Gmail 這類用 OIDC 整合的應用程式,走的是步驟 5~8。兩者最後換回來的東西不同(SAML Assertion 與 ID Token),但都是「驗證結果」的不同表達格式,整理如下:
| 比較項目 | SAML | OIDC |
|---|---|---|
| 資料格式 | XML | JSON(JWT) |
| 建立在什麼基礎上 | 獨立的協定 | OAuth 2.0 之上 |
| 常見場景 | 企業內部系統整合 | 現代 Web / 行動裝置、第三方登入 |
| 驗證結果 | SAML Assertion(附簽名) | ID Token(JWT,附簽名) |
看完整體差異後,這裡先深入歷史比較久的 SAML;OIDC 建立在 OAuth 2.0 之上,等之後把 OAuth 2.0 說明完,會再回來深入說明這部分。
SAML(Security Assertion Markup Language)
SAML 用 XML 格式描述一份「聲明(Assertion)」,裡面記錄了使用者身份、驗證時間等資訊,並附上簽名,歷史較久,常見於企業內部系統(例如 Salesforce)的 SSO 整合。目前的主流版本是 SAML 2.0(2005 年推出,整合了先前幾個版本),下面內容都是以 SAML 2.0 為準;較舊的系統可能還保留 SAML 1.1 以維持向下相容,但新系統基本不會再採用。一份 SAML Assertion 主要包含:
Issuer:簽發這份 Assertion 的 IdP 是誰。Subject:這份 Assertion 是關於哪一位使用者(通常是一個 Email 或內部帳號 ID)。Conditions:這份 Assertion 的有效範圍,例如有效期間(NotBefore/NotOnOrAfter)、限定給哪個 SP 使用(AudienceRestriction)。AuthnStatement:記錄使用者是什麼時候、用什麼方式完成驗證的(例如密碼、MFA)。AttributeStatement(可選):使用者的其他屬性,例如 Email、部門、角色,SP 可以拿這些屬性做進一步的權限判斷。簡化過的 Assertion 大致長這樣:
<saml:Assertion>
<saml:Issuer>https://idp.example.com</saml:Issuer>
<saml:Subject>
<saml:NameID>user@example.com</saml:NameID>
</saml:Subject>
<saml:AuthnStatement AuthnInstant="2026-08-26T06:00:00Z" />
<saml:AttributeStatement>
<saml:Attribute Name="role">
<saml:AttributeValue>admin</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
實務上最常見的是用 SAML Assertion 來表達「這個使用者已經完成身份驗證」,同時也可能夾帶 Email、部門、角色等屬性,讓 SP 後續建立帳號或判斷權限。
實際傳輸時,SAML 定義了兩種常見的Binding(傳輸方式):
AuthnRequest,也就是「請幫我驗證這個人」的請求),直接放進 URL 查詢參數裡重新導向。另外,SAML 流程裡常會看到一個參數叫做 RelayState。它可以想成「登入前使用者原本想去哪裡」的暫存資訊。當使用者被 SP 導向 IdP 登入時,SP 可以把原本要前往的頁面或狀態放進 RelayState;等 IdP 驗證完成、把使用者導回 SP 後,SP 就能根據 RelayState 把使用者帶回原本想看的頁面。
除了改善使用者體驗,RelayState 也可以用來協助確認這次回來的登入結果,是不是對應到先前由 SP 發起的那次請求,避免流程被混淆或濫用。
流程還可以再分成兩種發起方式:
AuthnRequest(即「請幫我驗證這個人」的請求) 導向 IdP,也就是前面第三節介紹過的流程。AuthnRequest。這種方式比較方便,但安全性稍低——因為沒有 SP 主動發出的請求可以互相對應,SP 只能單方面信任 IdP 送來的 Assertion,比較難確認這份 Assertion 是不是專門為這次存取產生、而不是被別人攔截後拿來重複使用;實作上需要額外檢查 Assertion 的時效性與指定的目標 SP,避免遭到重放攻擊。風險:因為 Assertion 裡帶有使用者身份等敏感資訊,SAML 規範要求 Assertion 必須簽名,防止內容被竄改;如果內容本身也需要保密(不想讓中間的瀏覽器或代理看到),還可以進一步加密整份 Assertion。
OIDC(OpenID Connect)
OIDC 建立在 OAuth 2.0 這個授權框架上,IdP 驗證完之後會回傳一個 ID Token——也就是一種 JWT,裡面用 sub、iss、exp 等欄位描述使用者身份,SP 驗證簽名就能相信內容。常見於現代 Web、行動裝置的登入整合(例如「使用 Google 帳號登入」)。
這篇先知道它和 SAML 一樣,都是 IdP 用來把「使用者已經驗證通過」這件事告訴 SP 的方式就好。至於 OIDC 為什麼建立在 OAuth 2.0 之上、Authorization Code Flow 怎麼換回 ID Token、ID Token 和 Access Token 又差在哪裡,後面會獨立開一篇再完整說明。
除了 SAML、OIDC,企業內部網路環境也常見像 Kerberos 這種以票證(Ticket)為基礎的協定,之後再深入展開。
這裡也順便澄清一個常被混淆的概念:LDAP(Lightweight Directory Access Protocol) 跟 SAML、OIDC 常被放在一起講,但它其實不是同一層次的東西:
換句話說,LDAP 發生在 IdP 內部(拿來查帳密資料),SAML/OIDC 發生在 IdP 跟 SP 之間(拿來傳送驗證結果)。同一次登入流程裡,兩種可能都會用到(例如企業內部的 IdP 背後接一個 LDAP 目錄來存放帳密),但做的事完全不同層次,這裡先點出差異,其他細節之後再另外深入。
風險:
相關概念辨析(常被搞混):
SSO 想解決的問題,是讓使用者完成一次身份驗證後,就能進入多個彼此信任的應用程式,不必在每個系統重複輸入帳密。不過,SSO 本身描述的是「一次登入、多處通行」的使用體驗,真正讓這件事成立的,是 IdP 與 SP 之間事先建立的信任關係,以及 SAML、OIDC 等協定所傳遞的驗證結果。
在整個流程中,IdP 負責驗證使用者並維護全域登入狀態;SP 不直接處理使用者的帳密,而是驗證 IdP 回傳的 Assertion 或 ID Token,再建立自己的本地 Session。也就是說,SSO 並不是讓所有系統共用同一份 Session,而是讓多個 SP 都信任同一個 IdP。
以下整理的重點:
不過,IdP 要完成身份驗證,背後仍然需要一個地方保存帳號、群組與組織資料,也需要一套方式證明使用者身份。下一篇會進一步介紹企業環境中常見的 LDAP 與 Kerberos,看看它們如何分別負責目錄查詢與票證驗證,又為什麼至今仍是 Active Directory 的重要基礎。