Tomcat Realm 配置

2022-03-03 11:45 更新

快速入門

本文檔介紹了如何借助一個“數(shù)據(jù)庫”來配置 Tomcat ,從而實現(xiàn)容器管理安全性。所要連接的這種數(shù)據(jù)庫含有用戶名、密碼以及用戶角色。你只需知道的是,如果使用的 Web 應用含有一個或多個 <security-constraint> 元素,<login-config> 元素定義了用戶驗證的必需細節(jié)信息。如果你不打算使用這些功能,則可以忽略這篇文檔。

關于容器管理安全性的基礎知識,可參考 Servlet Specification (Version 2.4) 中的第 12 節(jié)內容。

關于如何使用 Tomcat 中的單點登錄(用戶只需驗證一次,就能夠登錄一個虛擬主機的所有 Web 應用)功能,請參看該文檔。

概述

什么是 Realm

Realm(安全域)其實就是一個存儲用戶名和密碼的“數(shù)據(jù)庫”再加上一個枚舉列表?!皵?shù)據(jù)庫”中的用戶名和密碼是用來驗證 Web 應用(或 Web 應用集合)用戶合法性的,而每一合法用戶所對應的角色存儲在枚舉列表中。可以把這些角色看成是類似 UNIX 系統(tǒng)中的 group(分組),因為只有能夠擁有特定角色的用戶才能訪問特定的 Web 應用資源(而不是通過對用戶名列表進行枚舉適配)。特定用戶的用戶名下可以配置多個角色。

雖然 Servlet 規(guī)范描述了一個可移植機制,使應用可以在 web.xml 部署描述符中聲明它們的安全需求,但卻沒有提供一種可移植 API 來定義出 Servlet 容器與相應用戶及角色信息的接口。然而,在很多情況下,非常適于將 Servlet 容器與一些已有的驗證數(shù)據(jù)庫或者生產環(huán)境中已存在的機制“連接”起來。因此,Tomcat 定義了一個 Java 接口(org.apache.catalina.Realm),通過“插入”組件來建立連接。提供了 6 種標準插件,支持與各種驗證信息源的連接:

  • JDBCRealm——通過 JDBC 驅動器來訪問保存在關系型數(shù)據(jù)庫中的驗證信息。
  • DataSourceRealm——訪問保存在關系型數(shù)據(jù)庫中的驗證信息。
  • JNDIRealm——訪問保存在 LDAP 目錄服務器中的驗證信息。
  • UserDatabaseRealm——訪問存儲在一個 UserDatabase JNDI 數(shù)據(jù)源中的認證信息,通常依賴一個 XML 文檔(conf/tomcat-users.xml)。
  • MemoryRealm——訪問存儲在一個內存中對象集合中的認證信息,通過 XML 文檔初始化(conf/tomcat-users.xml)。
  • JAASRealm——通過 Java 認證與授權服務(JAAS)架構來獲取認證信息。

另外,還可以編寫自定義 Realm 實現(xiàn),將其整合到 Tomcat 中,只需這樣做:

  • 實現(xiàn) org.apache.catalina.Realm 接口。
  • 將編譯好的 realm 放到 $CATALINA_HOME/lib 中。
  • 聲明自定義 realm,具體方法詳見“配置 Realm”一節(jié)。
  • 在 MBeans 描述符文件中聲明自定義realm。

配置 Realm

在詳細介紹標準 Realm 實現(xiàn)之前,簡要了解 Realm 的配置方式是很關鍵的一步。大體來說,就是需要在conf/server.xml 配置文件中添加一個 XML 元素,如下所示:

<Realm className="... class name for this implementation"
       ... other attributes for this implementation .../>  

<Realm> 可以嵌入以下任何一種 Container 元素中。Realm 元素的位置至關重要,它會對 Realm 的“范圍”(比如說哪個 Web 應用能夠共享同一驗證信息)有直接的影響。

  • <Engine> 元素 內嵌入該元素中的這種 Realm 元素可以被所有虛擬主機上的所有 Web 應用所共享,除非該 Realm 元素被內嵌入下屬 <Host><Context> 元素的 Realm 元素所覆蓋。
  • <Host> 元素 內嵌入該元素中的這種 Realm 元素可以被這一虛擬主機上的所有 Web 應用所共享。除非該 Realm 元素被內嵌入下屬 <Context> 元素的 Realm 元素所覆蓋。
  • <Context> 元素 內嵌入該元素中的這種 Realm 元素只能被這一 Web 應用所使用。

常用特性

摘要式密碼

對于每種標準 Realm 實現(xiàn)來說,用戶的密碼默認都是以明文方式保存的。在很多情況下,這種方式都非常糟糕,即使是一般的用戶也能收集到足夠的驗證信息,從而以其他用戶的信息成功登錄。為了避免這種情況的發(fā)生,標準 Realm 實現(xiàn)支持一種對用戶密碼進行摘要式處理的機制,它能以無法輕易破解的形式對存儲的密碼進行加密處理,同時保證Realm 實現(xiàn)仍能使用這種加密后的密碼進行驗證。

在標準的 Realm 驗證時,會將存儲的密碼與用戶所提供的密碼進行比對,這時,我們可以通過指定 <Realm> 元素中的 digest 屬性來選擇摘要式密碼。該屬性值必須是一種java.security.MessageDigest 類所支持的摘要式算法(SHA、MD2、或 MD5)。當你選擇該屬性值時,存儲在 Realm 中的密碼內容必須是明文格式,隨后它將被你所指定的算法進行摘要式加密。

在調用 Realm 的 authenticate() 方法后,用戶所提供的明文密碼同樣也會利用上述你所指定的加密算法進行加密,加密結果與 Realm 的返回值相比較。如果兩者相等,則表明原始密碼的明文形式更用戶所提供的密碼完全等同,因此該用戶身份驗證成功。

可以采用以下兩種比較便利的方法來計算明文密碼的摘要值:

  • 如果應用需要動態(tài)計算摘要式密碼,調用 org.apache.catalina.realm.RealmBase 類的靜態(tài) Digest() 方法,傳入明文密碼和摘要式算法名稱及字符編碼方案。該方法返回摘要式密碼。
  • 如果想執(zhí)行命令行工具來計算摘要式密碼,只需執(zhí)行:
    CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}
    標準輸出將返回明文密碼的摘要式形式。

如果使用 DIGEST 驗證的摘要式密碼,用來生成摘要密碼的明文密碼則將有所不同,而且必須使用一次不加鹽的 MD5 算法。對應到上面的范例,那就是必須把 {cleartext-password} 替換成 {username}:{realm}:{cleartext-password}。再比如說,在一個開發(fā)環(huán)境中,可能采用這種形式:testUser:Authentication required:testPassword。{realm} 的值取自 Web 應用 <login-config><realm-name> 元素。如果沒有在 web.xml 中指定,則使用默認的Authentication required。

若要使用非平臺默認編碼的用戶名和(或)密碼,則命令如下:

CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} -e {encoding} {input}

但需要注意的是,一定要確保輸入正確地傳入摘要。摘要返回 {input}:{digest}。如果輸入在返回時出現(xiàn)損壞,摘要則將無效。

摘要的輸出格式為 {salt}${iterations}${digest}。如果鹽的長度為 0,迭代次數(shù)為 1,則輸出將簡化為 {digest}

CATALINA_HOME/bin/digest.[bat|sh] 的完整格式如下:

CATALINA_HOME/bin/digest.[bat|sh] [-a <algorithm>] [-e <encoding>]
        [-i <iterations>] [-s <salt-length>] [-k <key-length>]
        [-h <handler-class-name>] <credentials>  
  • -a 用來生成存儲憑證的算法。如未指定,將使用憑證處理器(CredentialHandler)的默認值,如果認證處理器和算法均未指定,則使用默認值 SHA-512。
  • -e 指定用于任何必要的字節(jié)與字符間轉換的字符編碼方案。如未指定,使用系統(tǒng)默認的字符編碼方案 Charset#defaultCharset()。
  • -i 生成存儲的憑證時所使用的迭代次數(shù)。如未指定,使用 CredentialHandler 的默認值。
  • -s 生成并存儲到認證中的鹽的長度(字節(jié))。如未指定,使用 CredentialHandler 的默認值。
  • -k (生成憑證時,如果隨帶創(chuàng)建了)鍵的長度(位)。如未指定,則使用 CredentialHandler 的默認值
  • -h CredentialHandler 使用的完整類名。如未指定,則輪流使用內建的憑證處理器(MessageDigestCredentialHandler,然后是 SecretKeyCredentialHandler),將使用第一個接受指定算法的憑證處理器。

范例應用

Tomcat 自帶的范例應用中包含一個受到安全限制保護的區(qū)域,使用表單式登錄方式。為了訪問它,在你的瀏覽器地址欄中輸入 http://localhost:8080/examples/jsp/security/protected/,并使用 UserDatabaseRealm 默認的用戶名和密碼進行登錄。

Manager 應用

如果你希望使用 Manager 應用在一個運行的 Tomcat 安裝上來部署或取消部署 Web 應用,那么必須在一個選定的 Realm 實現(xiàn)上,將 manager-gui 角色添加到至少一個用戶名上。這是因為 Manager 自身使用一個安全限制,要想在該應用的 HTML 界面中訪問請求 URI,就必須要有 manager-gui 角色。

出于安全性考慮,默認情況下,Realm 中的用戶名(比如使用 conf/tomcat-users.xml)沒有被分配 manager-gui 角色。因此,用戶起初無法使用這個功能,除非 Tomcat 管理員特意將這一角色分配給他們。

Realm 日志

Realm 的容器(Context、Host 及 Engine)所對應的日志配置文件將記錄 Realm 所記錄下的調試和異常信息。

標準 Realm 實現(xiàn)

JDBCRealm

簡介

JDBCRealm 是 Tomcat Realm 接口的一種實現(xiàn),它通過 JDBC 驅動程序在關系型數(shù)據(jù)庫中查找用戶。只要數(shù)據(jù)庫結構符合下列要求,你可以通過大量的配置來靈活地修改現(xiàn)有的表與列名。

  • 必須有一張用戶表users table)。它包含著一個由 Realm 所能識別的所有合法用戶所構成的行。
  • 用戶表必須至少包含兩列(當然,如果現(xiàn)有應用確實需要,則同樣也可以包含更多的列):
    • 用戶名。當用戶登錄時,能被 Tomcat 識別的用戶名。
    • 密碼。當用戶登錄時,能被 Tomcat 所識別的密碼。該列中的值可能是明文,也可能是摘要式密碼,稍后詳述。
  • 必須有一張用戶角色表user roles table)。該表包含一個角色行,包含著可能指定給特定用戶的每個合法角色。一個用戶可以沒有角色,也可以有一個或多個角色,這都是合法的。
  • 用戶角色表 至少應包含兩列(如果現(xiàn)有應用確實需要,則也可以包含更多的列):
    • 用戶名。Tomcat 所能識別的用戶名(與用戶表中指定的值相同)。
    • 用戶所對應的合法角色名。

快速入門

為了設置 Tomcat 從而使用 JDBCRealm,需要執(zhí)行以下步驟:

  1. 在數(shù)據(jù)庫中創(chuàng)建符合上述規(guī)范的表與列。
  2. 配置一個 Tomcat 使用的數(shù)據(jù)庫用戶名與密碼,并且至少有只讀權限(Tomcat 永遠都不會去修改那些表中的數(shù)據(jù))。
  3. 將用到的 JDBC 驅動程序復制到 $CATALINA_HOME/lib 目錄中。注意只能識別 JAR 文件!
  4. $CATALINA_BASE/conf/server.xml 目錄中設置一個 <Realm> 元素。這一點下文將會詳細敘述。
  5. 如果 Tomcat 處于運行狀態(tài),則重啟它。

Realm 元素屬性

如上所述,為了配置 JDBCRealm,需要創(chuàng)建一個 Realm 元素,并把它放在 $CATALINA_BASE/conf/server.xml 文件中。JDBCRealm 的屬性都定義在 Realm 配置文檔中。

范例

下面這個 SQL 腳本范例創(chuàng)建了我們所需的表(根據(jù)你所用的數(shù)據(jù)庫,可以相應修改其中的語法)。

create table users (
  user_name         varchar(15) not null primary key,
  user_pass         varchar(15) not null
);

create table user_roles (
  user_name         varchar(15) not null,
  role_name         varchar(15) not null,
  primary key (user_name, role_name)
);

Realm 元素包含在默認的 $CATALINA_BASE/conf/server.xml 文件中(被注釋掉了)。在下面的范例中,有一個名為 authority 的數(shù)據(jù)庫,它包含上述創(chuàng)建的表,通過用戶名“dbuser”和密碼“dbpass”進行訪問。

<Realm className="org.apache.catalina.realm.JDBCRealm"
      driverName="org.gjt.mm.mysql.Driver"
   connectionURL="jdbc:mysql://localhost/authority?user=dbuser&amp;password=dbpass"
       userTable="users" userNameCol="user_name" userCredCol="user_pass"
   userRoleTable="user_roles" roleNameCol="role_name"/>

特別注意事項

JDBCRealm 必須遵循以下規(guī)則:

  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法,從而使任何對數(shù)據(jù)庫的即時修改(新用戶、密碼或角色改變,等等)都能立即生效。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。
  • 應用負責管理users(用戶表)和user roles(用戶角色表)中的信息。Tomcat 沒有提供任何內置功能來維護這兩種表。

DataSourceRealm

簡介

DataSourceRealm 是 Tomcat Realm 接口的一種實現(xiàn),它通過一個 JNDI 命名的 JDBC 數(shù)據(jù)源在關系型數(shù)據(jù)庫中查找用戶。只要數(shù)據(jù)庫結構符合下列要求,你可以通過大量的配置來靈活地修改現(xiàn)有的表與列名。

  • 必須有一張用戶表users table)。它包含著一個由 Realm 所能識別的所有合法用戶所構成的行。
  • 用戶表必須至少包含兩列(當然,如果現(xiàn)有應用確實需要,則同樣也可以包含更多的列):
    • 用戶名。當用戶登錄時,能被 Tomcat 識別的用戶名。
    • 密碼。當用戶登錄時,能被 Tomcat 所識別的密碼。該列中的值可能是明文,也可能是摘要式密碼,稍后詳述。
  • 必須有一張用戶角色表user roles table)。該表包含一個角色行,包含著可能指定給特定用戶的每個合法角色。一個用戶可以沒有角色,也可以有一個或多個角色,這都是合法的。
  • 用戶角色表 至少應包含兩列(如果現(xiàn)有應用確實需要,則也可以包含更多的列):
    • 用戶名。Tomcat 所能識別的用戶名(與用戶表中指定的值相同)。
    • 用戶所對應的合法角色名。

快速入門

為了設置 Tomcat 從而使用 DataSourceRealm,需要執(zhí)行以下步驟:

  1. 在數(shù)據(jù)庫中創(chuàng)建符合上述規(guī)范的表與列。
  2. 配置一個 Tomcat 使用的數(shù)據(jù)庫用戶名與密碼,并且至少有只讀權限(Tomcat 永遠都不會去修改那些表中的數(shù)據(jù))。
  3. 為數(shù)據(jù)庫配置一個 JNDI 命名的 JDBC DataSource。詳情可參考JNDI DataSource Example HOW-TO》應該鏈接至相應中文頁面》。
  4. $CATALINA_BASE/conf/server.xml 目錄中設置一個 <Realm> 元素。這一點下文將會詳細敘述。
  5. 如果 Tomcat 處于運行狀態(tài),則重啟它。

范例

下面這個 SQL 腳本范例創(chuàng)建了我們所需的表(根據(jù)你所用的數(shù)據(jù)庫,可以相應修改其中的語法)。

create table users (
  user_name         varchar(15) not null primary key,
  user_pass         varchar(15) not null
);

create table user_roles (
  user_name         varchar(15) not null,
  role_name         varchar(15) not null,
  primary key (user_name, role_name)
);

在下面的范例中,有一個名為 authority 的 MySQL 數(shù)據(jù)庫,它包含上述創(chuàng)建的表,通過名為 “java:/comp/env/jdbc/authority” 的 JNDI 命名的 JDBC 數(shù)據(jù)源來訪問。

<Realm className="org.apache.catalina.realm.DataSourceRealm"
   dataSourceName="jdbc/authority"
   userTable="users" userNameCol="user_name" userCredCol="user_pass"
   userRoleTable="user_roles" roleNameCol="role_name"/>

特別注意事項

使用 DataSourceRealm 時必須遵守下列規(guī)則:

  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法,從而使任何對數(shù)據(jù)庫的即時修改(新用戶、密碼或角色改變,等等)都能立即生效。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。
  • 應用負責管理users(用戶表)和user roles(用戶角色表)中的信息。Tomcat 沒有提供任何內置功能來維護這兩種表。

JNDIRealm

簡介

JNDIRealm 是 Tomcat Realm 接口的一種實現(xiàn),通過一個 JNDI 提供者1在 LDAP 目錄服務器中查找用戶。realm 支持大量的方法來使用認證目錄。

通常是可以使用 JNDI API 類的標準 LDAP 提供者。

a. 連接目錄

realm 與目錄服務器的連接是通過 connectionURL 配置屬性來定義的。這個 URL 的格式是通過 JNDI 提供者來定義的。它通常是個 LDAP URL,指定了所要連接的目錄服務器的域名,另外還(可選擇)指定了所需的根命名上下文的端口和唯一名稱(DN)。

如果有多個提供者,則可以配置 alternateURL。如果一個套接字連接無法傳遞給提供者 connectionURL,則會換用 alternateURL。

當通過創(chuàng)建連接來搜索目錄,獲取用戶及角色信息時,realm 會利用 connectionNameconnectionPassword 這兩個屬性所指定的用戶名和密碼在目錄上進行自我認證。如果未指定這兩個屬性,則創(chuàng)立的連接是匿名連接,這種連接適用于大多數(shù)情況。

b. 選擇用戶目錄項

在目錄中,每個可被認證的用戶都必須表示為獨立的項,這種獨立項對應著由屬性 connectionURL 定義的初始 DirContext 中的元素。這種用戶項必須有一個包含認證所需用戶名的屬性。

每個用戶項的唯一性名稱(DN)通常含有用于認證的用戶名。在這種情況下,userPattern 屬性可以用來指定 DN,其中的 {0} 代表用戶名應該被替換的位置。

realm 必須搜索目錄來尋找一個包含用戶名的唯一項,可用下列屬性來配置搜索:

  • userBase 用戶子樹的基準項。如果未指定,則搜索基準為頂級元素。
  • userSubtree 用戶子樹,也就是搜索范圍。如果希望搜索以userBase 項為基準的整個子樹,則使用 true;默認值為 false,只對頂級元素進行搜索。
  • userSearch 指定替代用戶名之后所使用的 LDAP 搜索過濾器的模式。
c. 用戶認證
  • 綁定模式

    默認情況下,realm 會利用用戶項的 DN 與用戶所提供的密碼,將用戶綁定到目錄上。如果成功執(zhí)行了這種簡單的綁定,那么就可以認為用戶認證成功。

    出于安全考慮,目錄可能保存的是用戶的摘要式密碼,而非明文密碼(參看摘要式密碼以獲知詳情)。在這種情況下,在綁定過程中,目錄會自動將用戶所提供的明文密碼加密為正確的摘要式密碼,以便后續(xù)和存儲的摘要式密碼進行比對。然而在綁定過程中,realm 并不參與處理摘要式密碼。不會用到 digest 屬性,如果設置了該屬性,也會被自動忽略。

  • 對比模式

    另外一種方法是,realm 從目錄中獲取存儲的密碼,然后將其與用戶所提供的值進行比對。配置方法是,在包含密碼的用戶項中,將 userPassword 屬性設為目錄屬性名。

    對比模式的缺點在于:首先,connectionNameconnectionPassword 屬性必須配置成允許 realm 讀取目錄中的用戶密碼。出于安全考慮,這是一種不可取的做法。事實上,很多目錄實現(xiàn)甚至都不允許目錄管理器讀取密碼。其次,realm 必須自己處理摘要式密碼,包括要設置所使用的具體算法、在目錄中表示密碼散列值的方式。但是,realm 可能有時又要訪問存儲的密碼,比如為了支持 HTTP 摘要式訪問認證(HTTP Digest Access Authentication,RFC 2069)。(注意,HTTP 摘要式訪問認證不同于之前討論過的在庫中存儲密碼摘要的方式。)

d. 賦予用戶角色

Realm 支持兩種方法來表示目錄中的角色:

  • 將角色顯式表示為目錄項

    通過明確的目錄項來表示角色。角色項通常是一個 LDAP 分組項,該分組項的一個屬性包含角色名稱,另一屬性值則是擁有該角色的用戶的 DN 名或用戶名。下列屬性配置了一個目錄搜索來尋找與認證用戶相關的角色名。

    • roleBase 角色搜索的基準項。如未指定,則基準項為頂級目錄上下文。
    • roleSubtree 搜索范圍。如果希望搜索以 roleBase 為基準項的整個子樹,則設為 true。默認值為 false,請求一個只包含頂級元素的單一搜索。
    • roleSearch 用于選擇角色項的 LDAP 搜索過濾器。它還可以(可選擇)包含用于唯一名稱的模式替換 {0}、用戶名的模式替換 {1},以及用戶目錄項屬性的模式替換 {2}。使用 userRoleAttribute 來指定提供 {2} 值的屬性名。
    • roleName 包含角色名稱的角色項屬性。
    • roleNested 啟用內嵌角色。如果希望在角色中內嵌角色,則設為 true。如果配置了該屬性,每一個新近找到的 roleName 和 DN 都將用于遞歸式的新角色搜索。默認值為 false。
  • 將角色表示為用戶項屬性

    將角色名稱保存為用戶目錄項中的一個屬性值。使用 userRoleName 來指定該屬性名稱。

當然,也可以綜合使用這兩種方法來表示角色。

快速入門

為了配置 Tomcat 使用 JNDIRealm,需要下列步驟:

  1. 確保目錄服務器配置中的模式符合上文所列出的要求。
  2. 必要時可以為 Tomcat 配置一個用戶名和密碼,對上文所提到的信息用于只讀訪問權限(Tomcat 永遠不會修改該信息。)
  3. 按照下文的方法,在 $CATALINA_BASE/conf/server.xml 文件中設置一個 <Realm> 元素。
  4. 如果 Tomcat 已經運行,則重啟它。

Realm 元素屬性

如上所述,為了配置 JDBCRealm,需要創(chuàng)建一個 Realm 元素,并把它放在 $CATALINA_BASE/conf/server.xml 文件中。JDBCRealm 的屬性都定義在 Realm 配置文檔中。

范例

在目錄服務器上創(chuàng)建適合的模式超出了本文檔的講解范圍,因為這是跟每個目錄服務器的實現(xiàn)密切相關的。在下面的實例中,我們將假定使用的是 OpenLDAP 目錄服務器的一個分發(fā)版(2.0.11 版或更新版本,可從 http://www.openldap.org處下載)。假設 slapd.conf 文件包含下列設置(除了其他設置之外)。

database ldbm
suffix dc="mycompany",dc="com"
rootdn "cn=Manager,dc=mycompany,dc=com"
rootpw secret

我們還假定 connectionURL,使目錄服務器與 Tomcat 運行在同一臺機器上。要想了解如何配置及使用 JNDI LDAP 提供者的詳細信息,請參看 http://docs.oracle.com/javase/7/docs/technotes/guides/jndi/index.html。

接下來,假定利用如下所示的元素(以 LDIF 格式)來填充目錄服務器。

# Define top-level entry
dn: dc=mycompany,dc=com
objectClass: dcObject
dc:mycompany

# Define an entry to contain people
# searches for users are based on this entry
dn: ou=people,dc=mycompany,dc=com
objectClass: organizationalUnit
ou: people

# Define a user entry for Janet Jones
dn: uid=jjones,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: jjones
sn: jones
cn: janet jones
mail: j.jones@mycompany.com
userPassword: janet

# Define a user entry for Fred Bloggs
dn: uid=fbloggs,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: fbloggs
sn: bloggs
cn: fred bloggs
mail: f.bloggs@mycompany.com
userPassword: fred

# Define an entry to contain LDAP groups
# searches for roles are based on this entry
dn: ou=groups,dc=mycompany,dc=com
objectClass: organizationalUnit
ou: groups

# Define an entry for the "tomcat" role
dn: cn=tomcat,ou=groups,dc=mycompany,dc=com
objectClass: groupOfUniqueNames
cn: tomcat
uniqueMember: uid=jjones,ou=people,dc=mycompany,dc=com
uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com

# Define an entry for the "role1" role
dn: cn=role1,ou=groups,dc=mycompany,dc=com
objectClass: groupOfUniqueNames
cn: role1
uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com

OpenLDAP 服務器。假定用戶使用他們的 uid(比如說 jjones)登錄應用,匿名連接已經足夠可以搜索目錄并獲取角色信息了:

<Realm   className="org.apache.catalina.realm.JNDIRealm"
     connectionURL="ldap://localhost:389"
       userPattern="uid={0},ou=people,dc=mycompany,dc=com"
          roleBase="ou=groups,dc=mycompany,dc=com"
          roleName="cn"
        roleSearch="(uniqueMember={0})"
/>

利用這種配置,通過在 userPattern 替換用戶名,realm 能夠確定用戶的 DN,然后利用這個 DN 和取自用戶的密碼將用戶綁定到目錄中,從而驗證用戶身份,然后搜索整個目錄服務器來找尋用戶角色。

現(xiàn)在假定希望用戶輸入電子郵件地址(而不是用戶 id)。在這種情況下,realm 必須搜索目錄找到用戶項。當用戶項被保存在多個子樹中,而這些子樹可能分別對應不同的組織單位或企業(yè)位置時,可能必須執(zhí)行一個搜索。

另外,假設除了分組項之外,你還想用用戶項的屬性來保存角色,那么在這種情況下,Janet Jones 對應的項可能如下所示:

dn: uid=jjones,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: jjones
sn: jones
cn: janet jones
mail: j.jones@mycompany.com
memberOf: role2
memberOf: role3
userPassword: janet

這個 realm 配置必須滿足以下新要求:

<Realm   className="org.apache.catalina.realm.JNDIRealm"
     connectionURL="ldap://localhost:389"
          userBase="ou=people,dc=mycompany,dc=com"
        userSearch="(mail={0})"
      userRoleName="memberOf"
          roleBase="ou=groups,dc=mycompany,dc=com"
          roleName="cn"
        roleSearch="(uniqueMember={0})"
/>

當 Janet Jones 用她的電子郵件 j.jones@mycompany.com 登錄時,realm 會搜索目錄,尋找?guī)в性撾娻]值的唯一項,并嘗試利用給定密碼來綁定到目錄:uid=jjones,ou=people,dc=mycompany,dc=com。如果驗證成功,該用戶將被賦予以下三個角色:"role2" 與 "role3",她的目錄項中的 memberOf 屬性值;"tomcat",她作為成員存在的唯一分組項中的 cn 屬性值。

最后,為了驗證用戶,我們必須從目錄中獲取密碼并在 realm 中執(zhí)行本地比對,將 realm 按照如下方式來配置:

<Realm   className="org.apache.catalina.realm.JNDIRealm"
    connectionName="cn=Manager,dc=mycompany,dc=com"
connectionPassword="secret"
     connectionURL="ldap://localhost:389"
      userPassword="userPassword"
       userPattern="uid={0},ou=people,dc=mycompany,dc=com"
          roleBase="ou=groups,dc=mycompany,dc=com"
          roleName="cn"
        roleSearch="(uniqueMember={0})"
/>

但是,正如之前所討論的那樣,往往應該優(yōu)先考慮默認的綁定模式。

特別注意事項

使用 JNDIRealm 需要遵循以下規(guī)則:

  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法,從而使任何對數(shù)據(jù)庫的即時修改(新用戶、密碼或角色改變,等等)都能立即生效。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。
  • 應用負責管理users(用戶表)和user roles(用戶角色表)中的信息。Tomcat 沒有提供任何內置功能來維護這兩種表。

UserDatabaseRealm

UserDatabaseRealm 是 Tomcat Realm 接口的一種實現(xiàn),使用 JNDI 資源來存儲用戶信息。默認,JNDI 資源是通過一個 XML 文件來提供支持的。它并不是針對大規(guī)模生產環(huán)境用途而設計的。在啟動時,UserDatabaseRealm 會從一個 XML 文檔中加載所有用戶以及他們角色的信息(該 XML 文檔默認位于 $CATALINA_BASE/conf/tomcat-users.xml。)用戶、密碼以及相應角色通??衫? JMX 進行動態(tài)編輯,更改結果會加以保存并立刻反映在 XML 文檔中。

Realm 元素屬性

之前討論的一樣,為了配置 UserDatabaseRealm,需要在 $CATALINA_BASE/conf/server.xml 中創(chuàng)建 <Realm> 元素。關于 UserDatabaseRealm 中的屬性定義可參看 Realm 配置文檔

用戶文件格式

用戶文件使用的格式與 MemoryRealm所使用的相同。

范例

默認的 Tomcat 安裝已經配置了內嵌在 <Engine> 元素中的 UserDatabaseRealm,因而可以將其應用于所有的虛擬主機和 Web 應用中。默認的 conf/tomcat-users.xml 文件內容為:

<tomcat-users>
  <user username="tomcat" password="tomcat" roles="tomcat" />
  <user username="role1"  password="tomcat" roles="role1"  />
  <user username="both"   password="tomcat" roles="tomcat,role1" />
</tomcat-users>

特別注意事項

使用 UserDatabaseRealm 需要遵循以下規(guī)則:

  • 當 Tomcat 首次啟動時,它會從用戶文件中加載所有已定義的用戶及其相關信息。假如對該用戶文件中的數(shù)據(jù)進行修改,則只有重啟 Tomcat 后才能生效。這些修改并不是通過 UserDatabase 數(shù)據(jù)源來完成的,是由 Tomcat 所提供的通過 JMX 訪問的 MBean 來實現(xiàn)的。
  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。

MemoryRealm

簡介

MemoryRealm 是一種對 Tomcat 的 Realm 接口的簡單演示實現(xiàn),并不是針對生產環(huán)境而設計的。在啟動時,MemoryRealm 會從 XML 文檔中加載所有的用戶信息及其相關的角色信息(默認該文檔位于 $CATALINA_BASE/conf/tomcat-users.xml)。只有重啟 Tomcat 才能使對該文件作出的修改生效。

Realm 元素屬性

之前討論的一樣,為了配置 MemoryRealm,需要在 $CATALINA_BASE/conf/server.xml 中創(chuàng)建 <Realm> 元素。關于 MemoryRealm 中的屬性定義可參看 Realm 配置文檔。

用戶文件格式

用戶文件包含下列屬性。默認情況下,conf/tomcat-users.xml 必須是一個 XML 文件,并且?guī)в幸粋€根元素:<tomcat-users>。每一個有效用戶都有一個內嵌在根元素中的 <user> 元素。

  • name 用戶登錄所用的用戶名。
  • password 用戶登錄所用的密碼。如果 <Realm> 元素中沒有設置 digest 屬性,則采用明文密碼,否則就設置為摘要式密碼,如之前討論的那樣。
  • roles 以逗號分隔的用戶角色名列表。

特別注意事項

使用 MemoryRealm 需要注意以下規(guī)則:

  • 當 Tomcat 首次啟動時,它會從用戶文件中加載所有已定義的用戶及其相關信息。假如對該用戶文件中的數(shù)據(jù)進行修改,則只有重啟 Tomcat 后才能生效。
  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。
  • 應用負責管理users(用戶表)和user roles(用戶角色表)中的信息。Tomcat 沒有提供任何內置功能來維護這兩種表。

JAASRealm

簡介

JAASRealm 是 Tomcat 的 Realm 接口的一種實現(xiàn),通過 Java Authentication & Authorization Service(JAAS,Java身份驗證與授權服務)架構來實現(xiàn)對用戶身份的驗證。JAAS 架構現(xiàn)已加入到標準的 Java SE API 中。

通過 JAASRealm,開發(fā)者實際上可以將任何安全的 Realm 與 Tomcat 的 CMA 一起組合使用。

JAASRealm 是 Tomcat 針對基于 JAAS 的 J2EE 1.4 的 J2EE 認證框架的原型實現(xiàn),基于 JCP Specification Request 196,從而能夠增強容器管理安全性,并且能促進“可插拔的”認證機制,該認證機制能夠實現(xiàn)與容器的無關性。

根據(jù) JAAS 登錄模塊和準則(參見 javax.security.auth.spi.LoginModulejavax.security.Principal 的相關說明),你可以自定義安全機制,或者將第三方的安全機制與 Tomcat 所實現(xiàn)的 CMA 相集成。

快速入門

為了利用自定義的 JAAS 登錄模塊使用 JAASRealm,需要執(zhí)行如下步驟:

  1. 編寫自己的 JAAS 登錄模塊。在開發(fā)自定義登錄模塊時,將通過 JAAS 登錄上下文對基于 JAAS 2的 User 和 Role 類管理。注意,JAASRealm 內建的 CallbackHandler 目前只能識別 NameCallbackPasswordCallback。

    2. 詳情請參看 JAAS 認證教程JAAS 登錄模塊開發(fā)教程。

  2. 盡管 JAAS 并未明確指定,但你也應該為用戶和角色創(chuàng)建不同的類來加以區(qū)分,它們都應該擴展自 javax.security.Principal,從而使 Tomcat 明白從登錄模塊中返回的規(guī)則究竟是用戶還是角色(參看 org.apache.catalina.realm.JAASRealm 相關描述)。不管怎樣,第一個返回的規(guī)則被認為是用戶規(guī)則。
  3. 將編譯好的類指定在 Tomcat 的類路徑中。
  4. 為 Java 建立一個 login.config 文件(參見 JAAS LoginConfig 文件)。將其位置指定給 JVM,從而便于 Tomcat 明確它的位置。例如,設置如下環(huán)境變量:
    JAVA_OPTS=$JAVA_OPTS -Djava.security.auth.login.config==$CATALINA_BASE/conf/jaas.config

  5. 為了保護一些資源,在 web.xml 中配置安全限制。
  6. 在 server.xml 中配置 JAASRealm 模塊。
  7. 重啟 Tomcat(如果它正在運行)。

Realm 元素屬性

在上述步驟中,為了配置步驟 6 以上的 JAASRealm,需要創(chuàng)建一個 <Realm> 元素,并將其內嵌在 <Engine> 元素中的 $CATALINA_BASE/conf/server.xml 文件內。關于 JAASRealm 中的屬性定義可參看 Realm 配置文檔。

范例

下例是 server.xml 中的一截代碼段:

<Realm className="org.apache.catalina.realm.JAASRealm"
                appName="MyFooRealm"
    userClassNames="org.foobar.realm.FooUser"
     roleClassNames="org.foobar.realm.FooRole"/>

完全由登錄模塊負責創(chuàng)建并保存用于表示用戶規(guī)則的 User 與 Role 對象(javax.security.auth.Subject)。如果登錄模塊不僅無法創(chuàng)建用戶對象,而且也無法拋出登錄異常,Tomcat CMA 就會失去作用,所在頁面就會變成 http://localhost:8080/myapp/j_security_check 或其他未指明的頁面。

JAAS 方法具有雙重的靈活性:

  • 你可以在自定義的登錄模塊后臺執(zhí)行任何所需的進程。
  • 通過改變配置以及重啟服務器,你可以插入一個完全不同的登錄模塊,不需要對應用做出任何改動。

特別注意事項

  • 當用戶首次訪問一個受保護資源時,Tomcat 會調用這一 Realmauthenticate() 方法。
  • 一旦用戶認證成功,在登錄后,該用戶(及其相應角色)就將緩存在 Tomcat 中。(對于以表單形式的認證,這意味著直到會話超時或者無效才會過期;對于基本形式的驗證,意味著直到用戶關閉瀏覽器才會過期。)在會話序列化期間不會保存或重置緩存的用戶。對已認證用戶的數(shù)據(jù)庫信息進行的任何改動都不會生效,直到該用戶下次登錄。
  • 和其他 Realm 實現(xiàn)一樣,如果 server.xml 中的 <Realm> 元素包含一個 digest 屬性,則支持摘要式密碼。JAASRealm 的 CallbackHandler 將先于將密碼傳回 LoginModule 之前,對密碼進行摘要式處理。

CombinedRealm

簡介

CombinedRealm 是一種 Tomcat 的 Realm 實現(xiàn),通過一個或多個子 Realm 進行用戶驗證。

通過 CombinedRealm,開發(fā)者能夠將多個 Realm(同一或不同類型) 組合起來使用,從而用于驗證多種數(shù)據(jù)源,而且萬一當其中一個 Realm 失敗,或其他一些操作需要多個 Realm 時,它還能提供回滾處理。

子 Realm 是通過在定義 CombineRealm 的 Realm 元素中內嵌 Realm 元素來實現(xiàn)的。驗證操作會按照 Realm 元素的疊加順序來逐個進行。對逐個 Realm 進行驗證,從而就能充分證明用戶的身份。

Realm 元素屬性

為了配置 CombinedRealm,需要創(chuàng)建一個 <Realm> 元素,并將其內嵌在 <Engine><Host> 元素中的 $CATALINA_BASE/conf/server.xml 文件內。同樣,你也可以將其內嵌到 context.xml 文件下的 <Context> 節(jié)點。

范例

下面是 server.xml 中的一段代碼,綜合使用了 UserDatabaseRealm 和 DataSourceRealm:

<Realm className="org.apache.catalina.realm.CombinedRealm" >
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
   <Realm className="org.apache.catalina.realm.DataSourceRealm"
             dataSourceName="jdbc/authority"
             userTable="users" userNameCol="user_name" userCredCol="user_pass"
             userRoleTable="user_roles" roleNameCol="role_name"/>
</Realm>

LockOutRealm

簡介

LockOutRealm 是一個 Tomcat 的 Realm 實現(xiàn),它擴展了 CombinedRealm,假如在某一段時間內出現(xiàn)很多驗證失敗,則它能夠提供鎖定用戶的功能。

為了確保操作的正確性,該 Realm 允許出現(xiàn)較合理的同步。

該 Realm 并不需要對底層的 Realm 或與其相關的用戶存儲機制進行任何改動。它會記錄失敗的登錄,包括那些因為用戶不存在的登錄。為了防止無效用戶通過精心設計的請求而實施的 DOS 攻擊(從而造成緩存增加),沒有通過驗證的用戶所在列表的容量受到了嚴格的限制。

子 Realm 是通過在定義 LockOutRealm 的 Realm 元素中內嵌 Realm 元素來實現(xiàn)的。驗證操作會按照 Realm 元素的疊加順序來逐個進行。對逐個 Realm 進行驗證,從而就能充分證明用戶的身份。

Realm 元素屬性

為了配置 CombinedRealm,需要創(chuàng)建一個 <Realm> 元素,并將其內嵌在 <Engine><Host> 元素中的 $CATALINA_BASE/conf/server.xml 文件內。同樣,你也可以將其內嵌到 context.xml 文件下的 <Context> 節(jié)點。關于 LockOutRealm 中的屬性定義可參看 Realm 配置文檔。

范例

下面是 server.xml 中的一段代碼,為 UserDatabaseRealm 添加了鎖定功能。

<Realm className="org.apache.catalina.realm.LockOutRealm" >
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
</Realm>
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號