快速开始
本文档介绍了如何通过连接到现有的用户名、密码和用户角色的“数据库”来配置 Tomcat 以支持容器托管的安全性。 仅当使用的 Web 应用程序包含一个或多个<security-constraint>元素, 并且一个<login-config>元素定义用户如何进行身份验证时,才需要关心这一点。 如果未使用这些功能,则可以安全地跳过本文档。
有关容器管理的安全性的基本背景信息,请参阅 Servlet 规范(版本 2.4)的第 12 节。
有关使用 Tomcat 的单点登录功能(允许用户在与虚拟主机关联的整个 Web 应用程序集中对自己进行身份验证)的信息, 请参阅 此处。
概述
什么是 Realm?
Realm 是一个用户名和密码的“数据库”,用于识别 Web 应用程序(或一组 Web 应用程序)的有效用户, 以及与每个有效用户关联的角色列表的枚举。可以将角色视为类似于类 Unix 操作系统中的组, 因为对特定 Web 应用程序资源的访问权限被授予拥有特定角色的所有用户(而不是枚举关联用户名的列表)。 特定用户可以拥有任意数量的与其用户名关联的角色。
尽管 Servlet 规范描述了一种可移植机制,供应用程序声明其安全要求(在 web.xml 部署描述符中),
但没有可移植 API 来定义 Servlet 容器与关联的用户和角色信息之间的接口。
但是,在许多情况下,最好将 Servlet 容器“连接”到生产环境中已经存在的某个现有身份验证数据库或机制。
因此,Tomcat 定义了一个 Java 接口 (org.apache.catalina.Realm
),
可以通过 “插件” 组件来实现该接口来建立此连接。提供了 6 个标准插件,支持连接到各种身份验证信息源:
-
DataSourceRealm - 访问存储在关系数据库中的身份验证信息,通过命名的 JNDI JDBC DataSource 进行访问。
-
JNDIRealm - 访问存储在基于 LDAP 的目录服务器中的身份验证信息,通过 JNDI 提供程序进行访问。
-
UserDatabaseRealm - 访问存储在 UserDatabase JNDI 资源中的身份验证信息,该资源通常由 XML 文档 (
conf/tomcat-users.xml
) 提供支持。 -
MemoryRealm - 访问存储在内存中对象集合中的身份验证信息,该对象集合从 XML 文档 (
conf/tomcat-users.xml
) 初始化。 -
JAASRealm - 通过Java认证和授权服务(JAAS)框架访问认证信息。
也可以编写自己的 Realm 实现,并将其与 Tomcat 集成。为此需要:
-
实现
org.apache.catalina.Realm
-
将编译后的 Realm 放在
$CATALINA_HOME/lib
中 -
按照下面的 “配置 Realm” 部分所述声明 Realm
-
向 MBeans Descriptors 声明 Realm
配置 Realm
在深入了解标准 Realm 实现的细节之前,一般性地了解 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> 。
-
元素内部 <Host> - 此 Realm 将在此虚拟主机的所有 Web 应用程序之间共享,除非它被嵌套在从属元素内的 Realm 元素所覆盖<Context>。
-
在<Context>元素内部 - 此 Realm 将仅用于此 Web 应用程序。
共同特点
摘要式密码
对于每个标准的 Realm 实现,用户的密码(默认情况下)都是以明文形式存储的。 在许多环境中,这是不可取的,因为身份验证数据的临时观察者可以收集足够的信息来成功登录,并模拟其他用户。 为了避免这个问题,标准实现支持摘要用户密码的概念。 这允许对密码的存储版本进行编码(以一种不容易逆转的形式),但是 Realm 实现仍然可以用于身份验证。
当标准领域通过检索存储的密码并将其与用户提供的值进行比较来进行身份验证时, 可以通过在元素中放置 CredentialHandler 元素来选择摘要式密码<Realm>。 支持 SSHA、SHA 或 MD5 算法之一的一个简单选择是使用 MessageDigestCredentialHandler。 此元素必须配置为 java.security.MessageDigest 类支持的摘要算法之一(SSHA、SHA 或 MD5)。 当选择此选项时,存储在 Realm 中的密码内容必须是密码的明文版本,由指定的算法进行摘要。
当 Realm 的 authenticate() 方法被调用时,用户指定的(明文)密码本身会由相同的算法进行摘要, 并将结果与 Realm 返回的值进行比较。相等匹配意味着原始密码的明文版本与用户提供的明文版本相同,因此应授权此用户。
要计算明文密码的摘要值,支持两种便捷技术:
-
如果正在编写需要动态计算摘要密码的应用程序,请调用 org.apache.catalina.realm.RealmBase 类的静态 Digest() 方法,将明文密码、摘要算法名称和编码作为参数传递。此方法将返回摘要后的密码。
-
如果要执行命令行实用程序来计算摘要密码,只需执行
CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}
并且此明文密码的摘要版本将返回到 Standard Output。
如果将摘要式密码与 DIGEST 身份验证一起使用,则用于生成摘要的明文是不同的,
并且摘要必须使用 MD5 算法的一次迭代,没有盐。
在上面的示例中,{cleartext-password} 必须替换为 {username}:{realm}:{cleartext-password}
。
例如,在开发环境中,这可能采用 testUser:Authentication required:testPassword
的形式。
{realm} 的值取自 <realm-name> Web 应用程序的 <login-config>。
如果未在 web.xml 中指定,则使用默认值 Authentication required (需要身份验证)。
支持使用平台默认编码以外的编码的用户名和密码
CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} -e {encoding} {input}
但需要注意确保 input 正确传递到 digester。
摘要程序返回 {input}:{digest}
。如果输入在返回中显示已损坏,则摘要将无效。
摘要的输出格式为 {salt}${iterations}${digest}
。
如果盐长度为零且迭代计数为 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>] [-f <password-file> | <credentials>]
-
-a - 用于生成存储凭证的算法。如果未指定,则将使用处理程序的默认值。如果既未指定处理程序也未指定算法,则将使用默认值 SHA-512
-
-e - 用于可能需要的任何字节到/从字符转换的编码。如果未指定,则将使用系统编码
Charset#defaultCharset()
。 -
-i - 生成存储的凭证时要使用的迭代次数。如果未指定,则将使用 CredentialHandler 的默认值。
-
-s - 要作为凭证的一部分生成和存储的盐的长度(以字节为单位)。如果未指定,则将使用 CredentialHandler 的默认值。
-
-k - 生成凭证时创建的密钥的长度(以位为单位),如果有。如果未指定,则将使用 CredentialHandler 的默认值。
-
-h - 要使用的 CredentialHandler 的完全限定类名。如果未指定,将依次测试内置处理程序(
MessageDigestCredentialHandler
,然后是`SecretKeyCredentialHandler`),并且将使用第一个接受指定算法的处理程序。 -
-f - 包含要编码的密码的文件的名称。文件中的每一行应仅包含一个密码。使用此选项将忽略其他密码输入。
示例应用程序
Tomcat 附带的示例应用程序包括一个受安全约束保护的区域,该区域利用基于表单的登录。 要访问它,请将浏览器指向 http://localhost:8080/examples/jsp/security/protected/ 并使用为默认 UserDatabaseRealm 描述的用户名和密码之一登录。
Manager 应用程序
如果希望使用 Manager 应用程序来部署和取消部署正在运行的 Tomcat 安装中的应用程序, 那么必须将 “manager-gui” 角色添加到选择的 Realm 实现中的至少一个用户名中。 这是因为管理器 Web 应用程序本身使用一个安全约束,该约束要求角色 “manager-gui” 访问该应用程序的 HTML 界面中的任何请求 URI。
出于安全原因,默认 Realm 中的用户名(即使用 conf/tomcat-users.xml
不会被分配 “manager-gui” 角色)。
因此,在 Tomcat 管理员将此角色专门分配给一个或多个用户之前,任何人都无法使用此应用程序的功能。
Realm 日志记录
Realm 数据库记录的调试和异常消息将由与该领域的容器相关联的日志记录配置进行记录:其周围的 Context、Host 或 Engine。
标准 Realm 实现
DataSourceRealm
介绍
DataSourceRealm 是 Tomcat Realm 接口的一个实现, 通过名为 JDBC DataSource 的 JNDI 访问关系数据库中查找用户。 只要数据库结构符合以下要求,就可以灵活地适应现有的表和列名称:
-
必须有一个表,下面引用为 users 表,其中包含该 Realm 应该识别的每个有效用户都包含一行。
-
users 表必须至少包含两列(如果现有应用程序需要,则可能包含更多列):
-
用户登录时 Tomcat 可识别的用户名。
-
用户登录时 Tomcat 识别的密码。此值可以是明文或摘要 - 有关更多信息,请参阅下文。
-
-
必须有一个表,下面称为 user roles 表,其中包含分配给特定用户的每个有效角色的一行。用户拥有零个、一个或多个有效角色是合法的。
-
user roles 表必须至少包含两列(如果现有应用程序需要,则可能包含更多列):
-
Tomcat 要识别的用户名(与 users 表中指定的值相同)。
-
与此用户关联的有效角色的 Role name。
-
快速开始
要设置 Tomcat 以使用 DataSourceRealm,需要执行以下步骤:
-
如果尚未这样做,请在数据库中创建符合上述要求的表和列。
-
配置供 Tomcat 使用的数据库用户名和密码,该用户名和密码至少对上述表具有只读访问权限。(Tomcat 永远不会尝试写入这些 table.)
-
为数据库配置名为 JDBC DataSource 的 JNDI。有关如何配置名为 JDBC DataSource 的 JNDI 的信息,请参阅 JNDI DataSource 示例操作方法。请务必适当地* 设置 Realm 的 localDataSource 属性,具体取决于 JNDI DataSource 的定义位置。
-
在 <Realm> $CATALINA_BASE/conf/server.xml 文件中设置一个元素,如下所述。
-
如果 Tomcat 已在运行,请重新启动它。
Realm 元素属性
要配置 DataSourceRealm,需要创建一个<Realm>元素并将其嵌套在 $CATALINA_BASE/conf/server.xml
文件中,
如上所述。DataSourceRealm 的属性在 Realm 配置文档中定义。
示例
用于创建所需表的示例 SQL 脚本可能如下所示(根据特定数据库的需要调整语法):
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 数据库的示例,该数据库配置了上述表,
并使用名为 “java:/comp/env/jdbc/authority
” 的 JNDI JDBC DataSource 进行访问。
<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 根据以下规则运行:
-
当用户第一次尝试访问受保护的资源时,Tomcat 将调用该 Realm 的 authenticate() 方法。 因此,直接对数据库所做的任何更改(新用户、更改的密码或角色等)都将立即反映出来。
-
用户通过身份验证后,该用户(及其关联的角色)将在用户登录期间缓存在 Tomcat 中。 (对于基于 FORM 的身份验证,这意味着直到会话超时或失效;对于 BASIC 身份验证,这意味着直到用户关闭其浏览器)。 缓存的用户不会在会话序列化之间保存和恢复。对于已通过身份验证的用户, 对数据库信息所做的任何更改都不会反映出来,直到该用户下次再次登录时。
-
管理 users and user roles 表中的信息是自己的应用程序的责任。Tomcat 不提供任何内置功能来维护用户和角色。
JNDIRealm
介绍
JNDIRealm 是 Tomcat Realm 接口的一种实现,用于查找 JNDI 提供程序 (通常是 JNDI API 类提供的标准 LDAP 提供程序)访问的 LDAP 目录服务器中的用户。 该领域支持使用目录进行身份验证的多种方法。
连接到目录
领域与目录的连接由 connectionURL 配置属性定义。 这是一个 URL,其格式由 JNDI 提供者定义。 它通常是一个 LDAP URL,用于指定要连接到的目录服务器的域名, 以及所需的根命名上下文的端口号和专有名称 (DN)(可选)。
如果有多个提供商,则可以配置 alternateURL。 如果无法在 connectionURL 处与提供程序建立套接字连接,则将尝试使用 alternateURL。
在建立连接以搜索目录并检索用户和角色信息时, 领域将使用 connectionName 和 connectionPassword 属性指定的用户名和密码向目录验证自身。 如果未指定这些属性,则连接是匿名的。这在许多情况下就足够了。
选择用户的目录条目
每个可以进行身份验证的用户都必须在目录中由一个单独的条目表示, 该条目对应于由 connectionURL 属性定义的初始 DirContext 中的元素。 此用户条目必须具有一个属性,其中包含用于身份验证的用户名。
通常,用户条目的专有名称包含用于身份验证的用户名,但对于所有用户来说都是相同的。 在这种情况下,userPattern 属性可用于指定 DN,其中 “{0}” 标记应替换用户名的位置。
否则,领域必须搜索目录以查找包含 username 的唯一条目。以下属性配置此搜索:
-
userBase - 作为包含用户的子树的基础的条目。如果未指定,则搜索库为顶级上下文。
-
userSubtree - 搜索范围。如果您希望搜索以 userBase 条目为根的整个子树,请设置为 true。默认值 false 请求仅包括顶级的单级搜索。
-
userSearch - 指定在替换用户名后要使用的 LDAP 搜索过滤器的模式。
对用户进行身份验证
-
绑定模式
默认情况下,领域通过使用该用户条目的 DN 和用户提供的口令绑定到目录来验证用户。 如果此简单绑定成功,则认为用户已经过身份验证。
出于安全原因,目录可能会存储用户密码的摘要,而不是明文版本(有关更多信息,请参见摘要密码)。 在这种情况下,作为简单绑定操作的一部分,目录会自动计算用户提供的纯文本密码的正确摘要, 然后再根据存储的值进行验证。因此,在 bind 模式下,领域不参与摘要处理。 digest 属性未被使用,如果设置,将被忽略。
-
比较模式
或者,领域可以从目录中检索存储的密码,并将其与用户提供的值显式进行比较。 通过将 userPassword 属性设置为包含密码的用户条目中的 directory 属性的名称来配置此模式。
比较模式有一些缺点。首先,必须配置 connectionName 和 connectionPassword 属性以允许领域读取目录中的用户密码。 出于安全原因,这通常是不可取的;事实上,许多目录实现甚至不允许目录管理器读取这些密码。 此外,领域必须自行处理密码摘要,包括所用算法的变化以及在目录中表示密码哈希的方法。 但是,领域有时可能需要访问存储的密码,例如支持 HTTP 摘要访问身份验证 (RFC 2069)。 (请注意,HTTP 摘要身份验证不同于如上所述在存储库中存储密码摘要以获取用户信息)。
为用户分配角色
目录领域支持两种方法来表示目录中的角色:
-
角色作为显式目录条目
角色可以由显式目录条目表示。角色条目通常是 LDAP 组条目, 其中一个属性包含角色的名称,另一个属性的值是该角色中用户的专有名称或用户名。 以下属性配置目录搜索以查找与经过身份验证的用户关联的角色名称:
-
roleBase - 角色搜索的基本条目。如果未指定,则搜索库为顶级目录上下文。
-
roleSubtree - 搜索范围。如果希望搜索以 roleBase 条目为根的整个子树,请设置为 true。默认值 false 请求单级搜索,仅包括顶级搜索。
-
roleSearch - 用于选择角色条目的 LDAP 搜索过滤器。它可选地包括模式替换“{0}”来表示可分辨名称和/或“{1}”来表示用户名和/或“{2}”来表示已验证用户的用户目录条目中的属性。使用 userRoleAttribute 指定为 “{2}” 提供值的属性的名称。
-
roleName - 角色条目中包含该角色名称的属性。
-
roleNested - 启用嵌套角色。如果要在角色中嵌套角色,请设置为 true。如果已配置,则将递归尝试每个新找到的 roleName 和可分辨名称以进行新的角色搜索。默认值为 false。
-
Roles 作为用户条目的属性
-
角色名称也可以作为用户目录条目中某个属性的值保存。使用 userRoleName 指定此属性的名称。
可以使用两种方法的组合来表示角色。
快速开始
要将 Tomcat 设置为使用 JNDIRealm,需要执行以下步骤:
-
确保为目录服务器配置的架构符合上面列出的要求。
-
如果需要,请配置供 Tomcat 使用的用户名和密码,该用户名和密码对上述信息具有只读访问权限。(Tomcat 永远不会尝试修改此信息)
-
在 <Realm> $CATALINA_BASE/conf/server.xml 文件中设置一个元素,如下所述。
-
如果 Tomcat 已在运行,请重新启动它。
Realm 元素属性
要配置 JNDIRealm,需要创建一个<Realm>元素并将其嵌套在 $CATALINA_BASE/conf/server.xml
文件中,如上所述。
JNDIRealm 的属性在 Realm 配置文档中定义。
举例
在目录服务器中创建适当的架构超出了本文档的范围,因为它对于每个目录服务器实现都是唯一的。 在下面的示例中,假设使用的是 OpenLDAP 目录服务器的发行版(版本 2.0.11 或更高版本), 该发行版可从 https://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 目录服务器的 Realm 元素示例可能如下所示, 假设用户使用他们的 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 来确定用户的可分辨名称, 通过使用此 DN 和从用户处收到的密码绑定到目录来进行身份验证,并搜索目录以查找用户的角色。
现在假设用户在登录时需要输入他们的电子邮件地址而不是他们的 userid。 在这种情况下,领域必须在目录中搜索用户的条目。 (当用户条目保存在可能对应于不同组织单位或公司位置的多个子树中时,也需要进行搜索)。
此外,假设除了组条目之外,还希望使用用户条目的属性来保存角色。 现在,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 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
” 身份登录时,
领域将在目录中搜索以该值作为其 mail 属性的唯一条目,
并尝试使用给定的口令以 uid=jjones,ou=people,dc=mycompany,dc=com
身份绑定到该目录。
如果身份验证成功,则会为他们分配三个角色:“role2”和“role3”,
即其目录条目中“memberOf”属性的值,以及“tomcat”,
即他们所属的唯一组条目中的“cn”属性的值。
最后,要通过从目录中检索密码并在领域中进行本地比较来验证用户, 可以使用如下领域配置:
<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})"
/>
但是,如上所述,身份验证的默认绑定模式通常是首选。
其他说明
JNDIRealm 根据以下规则运行:
-
当用户第一次尝试访问受保护的资源时,Tomcat 将调用该 Realm 的 authenticate() 方法。 因此,对目录所做的任何更改(新用户、更改的密码或角色等)都将立即反映出来。
-
用户通过身份验证后,该用户(及其关联的角色)将在用户登录期间缓存在 Tomcat 中。 (对于基于 FORM 的身份验证,这意味着直到会话超时或失效; 对于 BASIC 身份验证,这意味着直到用户关闭其浏览器)。 缓存的用户不会在会话序列化之间保存和恢复。对于已通过身份验证的用户, 对目录信息所做的任何更改都不会反映出来,直到该用户下次再次登录时。
-
管理目录服务器中的信息是自己的应用程序的责任。 Tomcat 不提供任何内置功能来维护用户和角色。
UserDatabaseRealm 数据库
介绍
UserDatabaseRealm 是 Tomcat Realm 接口的一种实现,它使用 JNDI 资源来存储用户信息。
默认情况下,JNDI 资源由 XML 文件提供支持。它不是为大规模生产使用而设计的。
在启动时,UserDatabaseRealm 从一个 XML 文档加载有关所有用户及其相应角色的信息
(默认情况下,这个文档是从 $CATALINA_BASE/conf/tomcat-users.xml
加载的)。
用户、他们的密码和他们的角色都可能动态编辑,通常是通过 JMX 进行的。
更改可能会被保存并反映在 XML 文件中。
Realm 元素属性
要配置 UserDatabaseRealm,需要创建一个<Realm>元素并将其嵌套在
$CATALINA_BASE/conf/server.xml
文件中,如上所述。
UserDatabaseRealm 的属性在 Realm 配置文档中定义。
用户文件格式
对于基于 XML 文件的 UserDatabase,users 文件使用与 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 根据以下规则运行:
-
当 Tomcat 首次启动时,会从 users 文件中加载所有定义的用户及其相关信息。 在重新启动 Tomcat 之前,将无法识别对此文件中的数据所做的更改。 可以通过 UserDatabase 资源进行更改。为此,Tomcat 提供了可通过 JMX 访问的 MBean。
-
当用户第一次尝试访问受保护的资源时,Tomcat 将调用该 Realm 的 authenticate() 方法。
-
一旦用户通过身份验证后,该用户将在用户登录期间在 Tomcat 中关联。 (对于基于 FORM 的身份验证,这意味着直到会话超时或失效;对于 BASIC 身份验证,这意味着直到用户关闭其浏览器)。 但是,与其他领域不同,用户角色仍将反映 UserDatabase 内容。如果从数据库中删除用户,则将其视为没有角色。 UserDatabaseRealm 的 useStaticPrincipal 属性可以用来缓存用户及其所有角色。 缓存的用户不会在会话序列化之间保存和恢复。当用户的主体对象因任何原因被序列化时, 也将被替换为具有不再反映数据库内容的角色的静态等效对象。
MemoryRealm (内存领域)
介绍
MemoryRealm 是 Tomcat Realm 接口的简单演示实现。它不是为生产用途而设计的。 在启动时,MemoryRealm 从 XML 文档加载有关所有用户及其相应角色的信息 (默认情况下,此文档从 $CATALINA_BASE/conf/tomcat-users.xml 加载)。 在重新启动 Tomcat 之前,无法识别对此文件中的数据的更改。
Realm 元素属性
要配置 MemoryRealm,需要创建一个<Realm>元素并将其嵌套在 $CATALINA_BASE/conf/server.xml
文件中,
如上所述。MemoryRealm 的属性在 Realm 配置文档中定义。
用户文件格式
用户文件(默认情况下, conf/tomcat-users.xml
必须是 XML 文档,具有 root 元素 <tomcat-users>
。
嵌套在根元素内的将是 <user> 每个有效用户的一个元素,由以下属性组成:
-
name - 此用户必须使用的用户名登录。
-
password - 此用户必须登录的密码(如果未在元素上设置 digest 属性,则以明文形式<Realm>登录,或者按照此处的其他说明进行适当摘要)。
-
roles - 与此用户关联的角色名称的逗号分隔列表。
其他说明
MemoryRealm 根据以下规则运行:
-
当 Tomcat 首次启动时,会从 users 文件中加载所有定义的用户及其相关信息。 在重新启动 Tomcat 之前,将无法识别对此文件中数据的更改。
-
当用户第一次尝试访问受保护的资源时,Tomcat 将调用该 Realm 的 authenticate() 方法。
-
用户通过身份验证后,该用户(及其关联的角色)将在用户登录期间缓存在 Tomcat 中。( 对于基于 FORM 的身份验证,这意味着直到会话超时或失效; 对于 BASIC 身份验证,这意味着直到用户关闭其浏览器)。 缓存的用户不会在会话序列化之间保存和恢复。
-
管理 users 文件中的信息是应用程序的责任。Tomcat 不提供任何内置功能来维护用户和角色。
JAASRealm
介绍
JAASRealm是Tomcat Realm接口的一种实现,通过Java认证和授权服务(JAAS)框架验证用户, 该框架现在已作为标准Java SE API的一部分提供。
使用 JAASRealm 使开发人员能够将几乎任何可以想象的安全领域与 Tomcat 的 CMA 相结合。
JAASRealm 是 J2EE v1.4 的基于 JAAS 的 J2EE 身份验证框架的 Tomcat 原型, 基于 JCP 规范请求 196,以增强容器管理的安全性并促进“可插拔”身份验证机制,其实现将与容器无关。
基于 JAAS 登录模块和主体(请参见`javax.security.auth.spi.LoginModule` 和 javax.security.Principal
),
可以开发自己的安全机制或包装另一个第三方机制,以便与 Tomcat 实现的 CMA 集成。
快速开始
要将 Tomcat 设置为将 JAASRealm 与自己的 JAAS 登录模块一起使用,需要执行以下步骤:
-
基于 JAAS 编写自己的 LoginModule、User 和 Role 类(请参见 JAAS 验证教程和 JAAS 登录模块开发人员指南), 以便由 JAAS 登录上下文 (
javax.security.auth.login.LoginContext
) 进行管理。 在开发 LoginModule 时,请注意 JAASRealm 的内置 CallbackHandler 目前仅识别 NameCallback 和 PasswordCallback。 -
尽管在 JAAS 中未指定,但应该创建单独的类来区分用户和角色,并扩展
javax.security.Principal
, 以便 Tomcat 可以分辨出从登录模块返回的哪些 Principal 是用户, 哪些是角色(参见org.apache.catalina.realm.JAASRealm
)。 无论如何,返回的第一个 Principal 始终被视为用户 Principal -
将编译后的类放在 Tomcat 的 Classpath 上
-
为 Java 设置一个
login.config 文件
(参见 JAAS LoginConfig 文件), 并通过向 JVM 指定其位置来告诉 Tomcat 在哪里可以找到它, 例如通过设置环境变量:JAVA_OPTS=$JAVA_OPTS -Djava.security.auth.login.config==$CATALINA_BASE/conf/jaas.config
-
在 web.xml 中为要保护的资源配置安全约束
-
在 server.xml 中配置 JAASRealm 模块
-
如果 Tomcat 已在运行,请重新启动
Realm 元素属性
要像上面的步骤 6 一样配置 JAASRealm,需要创建一个<Realm>元素并将其嵌套
在节点内的 $CATALINA_BASE/conf/server.xml
文件中<Engine>。
JAASRealm 的属性在 Realm 配置文档中定义。
举例
下面是 server.xml 代码段的外观示例。
<Realm className="org.apache.catalina.realm.JAASRealm"
appName="MyFooRealm"
userClassNames="org.foobar.realm.FooUser"
roleClassNames="org.foobar.realm.FooRole"/>
登录模块负责创建并保存表示用户主体的 User 和 Role 对象 (javax.security.auth.Subject
)。
如果登录模块没有创建用户对象,但也没有引发登录异常,则 Tomcat CMA 将中断,
将被留在 http://localhost:8080/myapp/j_security_check URI 或其他未指定的位置。
JAAS 方法的灵活性有两个方面:
-
可以在自己的登录模块中执行后台所需的任何处理。
-
可以通过更改配置并重新启动服务器来插入完全不同的 LoginModule,而无需对应用程序进行任何代码更改。
其他说明
-
当用户第一次尝试访问受保护的资源时,Tomcat 将调用该 Realm 的 authenticate() 方法。 因此,直接在安全机制中所做的任何更改(新用户、更改的密码或角色等)都将立即反映出来。
-
用户通过身份验证后,该用户(及其关联的角色)将在用户登录期间缓存在 Tomcat 中。 对于基于 FORM 的身份验证,这意味着直到会话超时或失效;对于 BASIC 身份验证,这意味着直到用户关闭其浏览器。 对已验证用户的安全信息所做的任何更改都不会反映出来,直到该用户下次再次登录时。
-
与其他 Realm 实现一样,如果 <Realm> server.xml 中的元素包含 digest 属性,则支持摘要密码; JAASRealm 的 CallbackHandler 将在将密码传回 LoginModule 之前 digest 密码
CombinedRealm
介绍
CombinedRealm 是 Tomcat Realm 接口的一种实现, 它通过一个或多个子 Realm 对用户进行身份验证。
使用 CombinedRealm 使开发人员能够组合相同或不同类型的多个 Realm。 这可用于针对不同的源进行身份验证,在一个 Realm 失败时提供回退, 或者用于需要多个 Realm 的任何其他目的。
子 Realm 是通过将 Realm 元素嵌套在定义 CombinedRealm 的 Realm 元素中来定义的。 将按照每个 Realm 的列出顺序尝试对每个 Realm 进行身份验证。 针对任何 Realm 的身份验证都足以对用户进行身份验证。
Realm 元素属性
要配置 CombinedRealm,需要创建一个<Realm>元素并将其嵌套在 $CATALINA_BASE/conf/server.xml
文件中的 <Engine> 或 <Host>中。还可以嵌套在 <Context> context.xml 文件中的节点内。
举例
下面是一个示例,说明 server.xml 代码片段应该如何使用 UserDatabase Realm 和 DataSource Realm。
<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 接口的实现,它扩展了 CombinedRealm 以提供锁定功能, 以便在给定时间段内身份验证尝试失败次数过多时提供用户锁定机制。
为了确保正确操作,此 Realm 中存在合理程度的同步。
此 Realm 不需要修改底层 Realm 或相关的用户存储机制。 通过记录所有失败的登录来实现此目的,包括不存在的用户的登录。 为了防止 DOS 通过考虑向无效用户发出请求(从而导致此缓存增长)来防止 DOS,验证失败的用户列表的大小是有限的。
子 Realm 是通过将 Realm 元素嵌套在定义 LockOutRealm 的 Realm 元素中来定义的。、将按照每个 Realm 的列出顺序尝试对每个 Realm 进行身份验证。针对任何 Realm 的身份验证都足以对用户进行身份验证。
Realm 元素属性
要配置 LockOutRealm,需要创建一个<Realm>元素并将其嵌套在
$CATALINA_BASE/conf/server.xml
文件中的 <Engine> 或 <Host>中。
还可以嵌套在 <Context> context.xml
文件中的节点内。
LockOutRealm 的属性在 Realm 配置文档中定义。
举例
下面是一个示例,展示了`server.xml`代码段如何向 UserDatabase Realm
添加锁定功能。
<Realm className="org.apache.catalina.realm.LockOutRealm" >
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>