知識庫 / Spring / Spring Security RSS 訂閱

SPNEGO/Kerberos 身份驗證在 Spring 中的介紹

Spring Security
HongKong
5
01:22 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將瞭解 Kerberos 身份驗證協議的基礎知識。我們還將探討 SPNEGO(RFC 4178)在 Kerberos 中的必要性。

最後,我們將看到如何利用 Spring Security Kerberos 擴展來創建啓用了 Kerberos 和 SPNEGO 的應用程序。

在繼續之前,值得注意的是,對於不熟悉該領域的初學者來説,本教程將介紹許多新的術語。因此,我們將花費一些時間來奠定基礎。

2. 理解 Kerberos

Kerberos 是麻省理工學院 (MIT) 在 80 年代初期開發的網絡身份驗證協議

正如您可能意識到,這相對較老,但經受住了時間的考驗。Windows Server 廣泛支持 Kerberos 作為身份驗證機制,甚至將其設置為默認身份驗證選項。

技術上講,Kerberos 是一種基於票據的身份驗證協議,允許計算機網絡中的節點相互識別自身。

2.1. Kerberos 簡單使用案例

讓我們通過一個假設情景來演示這一點。

假設一個用户通過其機器上的郵件客户端從另一台同一網絡上的郵件服務器拉取郵件。這裏顯然需要進行身份驗證。郵件客户端和郵件服務器必須能夠識別並信任彼此,才能安全地進行通信。

Kerberos 在這裏能提供什麼幫助? Kerberos 引入了一個名為密鑰分發中心 (KDC) 的第三方,該中心與網絡中的每個節點都建立相互信任關係。 讓我們看看它如何在我們的案例中發揮作用:

2.2. Kerberos 協議的關鍵要素

儘管這聽起來有些晦澀,但實際上它非常簡單且巧妙地在未受保護的網絡中安全地傳輸通信。在 TLS 無處不在的時代,這裏所提出的許多問題都已不再被重視!

雖然這裏不可能對 Kerberos 協議進行詳盡的討論,但我們來回顧一些關鍵要素:

  • 節點(客户端和服務器)之間以及 KDC 之間的信任在這裏被假設存在,並且在同一領域內
  • 密碼絕不會通過網絡交換
  • 客户端和服務器之間的信任是基於它們能夠僅與 KDC 共享密鑰來解密消息的事實而隱含的
  • 客户端和服務器之間的信任是相互的
  • 客户端可以緩存票據以供重複使用,直到過期,從而提供單點登錄體驗
  • 身份驗證器消息基於時間戳,因此僅適用於一次性使用
  • 這裏的三方必須具有相對同步的時間

雖然這只是對這款美妙的身份驗證協議的初步瞭解,但足以讓我們開始教程。

3. 理解 SPNEGO

SPNEGO 代表 簡單且受保護的 GSS-API 協商機制。 名字相當複雜! 首先讓我們看看 GSS-API 的含義。 通用安全服務應用程序編程接口 (GSS-API) 只是一個 IETF 標準,用於客户端和服務器以安全且與供應商無關的方式進行通信。

SPNEGO 是 GSS-API 的一部分,用於客户端和服務器協商使用的安全機制,例如 Kerberos 或 NTLM。

4. 為什麼我們需要 SPNEGO 與 Kerberos 結合使用?

正如我們在上一節中所看到的,Kerberos 是一種純粹的網絡身份驗證協議,主要在傳輸層(TCP/UDP)中運行。雖然這對於許多用例來説很好,但它無法滿足現代 Web 的要求。如果我們的應用程序運行在更高的抽象層,例如 HTTP,則無法直接使用 Kerberos。

這時,SPNEGO 就能派上用場。對於 Web 應用程序而言,通信主要發生在 Web 瀏覽器(如 Chrome)與 Web 服務器(如 Tomcat)通過 HTTP 託管 Web 應用程序之間。如果啓用,它們可以 通過 SPNEGO 進行安全協商並作為 SPNEGO 令牌交換機票

這如何改變我們之前提到的場景?讓我們用 Web 瀏覽器和郵件服務器替換我們的簡單郵件客户端:

""

因此,與我們之前的圖表相比,這裏變化不大,只是客户端和服務器之間的通信現在明確地發生在 HTTP 上。讓我們更好地理解這一點:

  • 客户端機向 KDC 進行身份驗證並緩存 TGT
  • 客户端機上的 Web 瀏覽器配置為使用 SPNEGO 和 Kerberos
  • Web 應用程序也配置為支持 SPNEGO 和 Kerberos
  • Web 應用程序向嘗試訪問受保護資源的 Web 瀏覽器發出“Negotiate”挑戰
  • 服務機票被包裝為 SPNEGO 令牌並作為 HTTP 標頭交換

5. 要求

在開始開發支持 Kerberos 身份驗證模式的 Web 應用程序之前,我們需要收集一些基本配置。我們快速地完成以下任務。

5.1. KDC 設置

設置用於生產環境的 Kerberos 環境超出了本教程的範圍。 這是一個不幸的是並非易事且容易出現問題的任務。 有多種選項可用於獲取 Kerberos 的實現,包括開源和商業版本:

KDC 的實際設置以及相關基礎設施取決於提供者,應按照其各自的文檔進行操作。 但是,Apache Kerby 可以通過 Docker 容器運行,從而使其具有平台無關性。

5.2. 在 KDC 中設置用户

我們需要在 KDC 中設置兩個用户,即“ principals”(通常也被稱為主體的名稱)。我們可以使用“kadmin”命令行工具來完成此操作。假設我們已經在 KDC 數據庫中創建了一個名為“baeldung.com”的域,並且已使用具有管理員權限的用户登錄到“kadmin”工具。

我們將創建我們的第一個用户,該用户將通過 Web 瀏覽器進行身份驗證,並使用以下配置:

$ kadmin: addprinc -randkey kchandrakant -pw password
Principal "[email protected]" created.

我們還需要將我們的 Web 應用程序註冊到 KDC 中:

$ kadmin: addprinc -randkey HTTP/[email protected] -pw password
Principal "HTTP/[email protected]" created.

請注意指定主機的命名約定,因為這必須與應用程序可從 Web 瀏覽器訪問的域相匹配。Web 瀏覽器在遇到“協商”挑戰時,會自動嘗試創建一個 Service Principal Name (SPN) ,遵循此約定。

此外,還需要將其導出為 keytab 文件,以便供 Web 應用程序使用。

$ kadmin: ktadd -k baeldung.keytab HTTP/[email protected]

這將生成一個名為“baeldung.keytab”的文件。

5.3. 瀏覽器配置

我們需要啓用用於訪問 Web 應用程序受保護資源的 Web 瀏覽器,以供“協商”身份驗證方案使用。 幸運的是,像 Chrome 這樣的大多數現代 Web 瀏覽器默認情況下都支持“協商”身份驗證方案。

此外,我們還可以配置瀏覽器以提供“集成身份驗證”。 在這種模式下,當瀏覽器遇到“協商”挑戰時,它會嘗試利用主機機器中已登錄的 KDC 主體的緩存憑據。 但是,我們在這裏不會使用這種模式,以保持明確性。

5.4. 域名配置

可以理解的是,我們可能沒有實際的域名進行測試我們的 Web 應用程序。但令人遺憾的是,我們無法使用 localhost、127.0.0.1 或任何其他帶有 Kerberos 身份驗證的 IP 地址。不過,這裏有一個簡單的解決方案,即在“hosts”文件中設置條目,例如:

demo.kerberos.bealdung.com 127.0.0.1

6. 拯救者到來了!

終於,在掌握了基本原理之後,我們該進行理論驗證了。但是,創建一個支持 SPNEGO 和 Kerberos 的 Web 應用程序會變得繁瑣嗎?如果使用 Spring,答案是否定的。 Spring 提供了 Kerberos 擴展作為 Spring Security 的一部分,可以無縫地支持 SPNEGO 與 Kerberos 的集成。

幾乎所有我們需要做的就是配置 Spring Security 以啓用 SPNEGO 與 Kerberos 的支持。 我們將使用 Java 風格的配置,但 XML 配置也可以輕鬆設置。

6.1. Maven 依賴

首先需要設置依賴項:

<dependency>
    <groupId>org.springframework.security.kerberos</groupId>
    <artifactId>spring-security-kerberos-web</artifactId>
    <version>${kerberos.extension.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security.kerberos</groupId>
    <artifactId>spring-security-kerberos-client</artifactId>
    <version>${kerberos.extension.version}</version>
</dependency>

這些依賴項可在 Maven Central 下載。

6.2. SPNEGO 配置

首先,SPNEGO 已集成到 Spring Security 中作為 FilterHTTPSecurity 中:

 @Override
 public void configure(HttpSecurity http) throws Exception {
     AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
     http.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManager),
         BasicAuthenticationFilter.class);
}

這僅顯示配置 SPNEGO 過濾器 的必要部分,並非完整的 HTTPSecurity 配置,應根據應用程序安全要求進行配置。

接下來,我們需要提供 SPNEGO 過濾器 作為 Bean

@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
  AuthenticationManager authenticationManager) {
    SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
    filter.setAuthenticationManager(authenticationManager);
    return filter;
}

6.3. Kerberos 配置

此外,我們還可以通過在 Spring Security 中將 AuthenticationProvider 添加到 AuthenticationManagerBuilder 來配置 Kerberos。

 @Bean
 public AuthenticationManager authManager(HttpSecurity http) throws Exception {
     return http.getSharedObject(AuthenticationManagerBuilder.class)
         .authenticationProvider(kerberosAuthenticationProvider())
         .authenticationProvider(kerberosServiceAuthenticationProvider())
         .build();
 }

首先我們需要提供一個 <em>KerberosAuthenticationProvider</em> 作為 <em>Bean</em>。這是一個 <em>AuthenticationProvider</em> 的實現,在這裏我們設置 <em>SunJaasKerberosClient</em> 作為 <em>KerberosClient</em>

@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
    KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
    SunJaasKerberosClient client = new SunJaasKerberosClient();
    provider.setKerberosClient(client);
    provider.setUserDetailsService(userDetailsService());
    return provider;
}

接下來,我們還需要提供一個 KerberosServiceAuthenticationProvider 作為 Bean。 這是驗證 Kerberos 服務票證或 SPNEGO 令牌的類:

@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
    KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
    provider.setTicketValidator(sunJaasKerberosTicketValidator());
    provider.setUserDetailsService(userDetailsService());
    return provider;
}

最後,我們需要提供一個 SunJaasKerberosTicketValidator 作為 Bean。這是一個 KerberosTicketValidator 的實現,並使用 SUN JAAS Login Module:

@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
    SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
    ticketValidator.setServicePrincipal("HTTP/[email protected]");
    ticketValidator.setKeyTabLocation(new FileSystemResource("baeldung.keytab"));
    return ticketValidator;
}

6.4. 用户詳情

我們之前在 AuthenticationProvider 中看到過 UserDetailsService 的引用,那麼為什麼我們需要它呢? 就像我們已經瞭解到 Kerberos 純粹是一種基於票據的身份驗證機制一樣。

因此,雖然它能夠識別用户,但它並不能提供與用户相關的其他信息,例如用户授權。 我們需要向 AuthenticationProvider 提供一個有效的 UserDetailsService,以填補這一空白。

6.5. 運行應用程序

此步驟基本上是設置啓用了 SPNEGO 的 Kerberos 的 Spring Security 驅動的 Web 應用程序所需要的。當啓動 Web 應用程序並訪問其中任何頁面時,Web 瀏覽器應提示用户輸入用户名和密碼,準備一個包含服務票據的 SPNEGO 令牌,並將其發送到應用程序。

應用程序應能夠使用 keytab 文件中的憑據處理它,並以成功身份驗證響應。

然而,正如我們之前所見,設置一個可工作的 Kerberos 環境既複雜又脆弱。如果事情沒有按預期進行,則值得檢查所有步驟。例如,域名不匹配等簡單錯誤會導致失敗,併產生不太有幫助的錯誤消息。

7. SPNEGO 和 Kerberos 的實際應用

現在我們已經瞭解了 Kerberos 認證的工作原理以及如何在 Web 應用程序中使用 SPNEGO 與 Kerberos,可能會質疑它存在的必要性。雖然它在企業網絡中作為單點登錄 (SSO) 機制使用完全合理,但為什麼我們應該在 Web 應用程序中使用它?

當然,即使在多年來,Kerberos 在企業應用程序中仍然被廣泛使用,尤其是在 Windows 應用程序中。如果一個組織擁有多個內部和外部 Web 應用程序,那麼將相同的 SSO 基礎設施擴展到這些應用程序中,無疑是有意義的。這使得組織中的管理員和用户能夠通過不同的應用程序獲得無縫體驗。

8. 結論

總結來説,在本教程中,我們理解了 Kerberos 身份驗證協議的基礎知識。我們還討論了 SPNEGO 作為 GSS-API 的一部分,以及如何利用它在 HTTP 上促進基於 Kerberos 的 Web 應用程序身份驗證。此外,我們嘗試構建一個利用 Spring Security 內置對 SPNEGO 的支持的小型 Web 應用程序。

本教程只是對一種強大且經過驗證的身份驗證機制的快速預覽。 還有大量的信息可供我們學習,並可能更深入地瞭解它!

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.