1.安全域
实现安全验证事务有两种方式:
- 完全有由Web应用本身来实现。
- 采用由Web服务器提供的通用的安全验证功能(如安全域)。
1.1 安全域概述
安全域是Web服务器用来保护Web应用的资源的一种机制。在安全域中可以配置安全验证信息,其用户信息(包括用户名和口令)以及用户和角色的映射关系。每个用户可以拥有一个或多个角色,每个角色限定了可访问的Web资源。
安全域是Tomcat内置的功能,在org.apache.catalina.Realm接口中声明了把一组用户名、口令以及所关联的角色集成到Tomcat中的方法。Tomcat为 Realm接口提供了一些实现类。下表列出了常见的一些 Realm实现类,它们代表不同的安全域类型。
| 安全域类型 | 类名 | 描述 |
|---|---|---|
| 内存域 | MemoryRealm | 在初始化阶段,从XML文件中读取安全验证信息,并把他们以一组对象的形式存放在内存中。 |
| JDBC域 | JDBCRealm | 通过JDBC驱动程序访问存放在数据库中的安全验证信息。 |
| 数据源域 | DataSourceRealm | 通过JNDI数据源访问存放在数据库中的安全验证信息。 |
| JNDI域 | JNDIRealm | 通过JNDI provider访问存放在基于LDAP的目录服务器中的安全验证信息。 |
| JAAS域 | JAASRealm | 利用JAAS(Java验证与授权服务)框架进行验证。 |
| 合并域 | CombinedRealm | 合并使用其他的安全域,从多种来源(例如XML文件或数据库)中获取安全验证信息。 |
不管配置哪一种类型的安全域,都包含以下步骤
在Web应用的 WEB-INF/web.xml 文件中为 Web资源设置安全约束。
在Tomcat的<CATALINA_HOME>/conf/server.xml配置文件中,或者 Web应用的META-INF/context.xml文件中配置<Realm>元素,形式如下:
<Realm className="...实现这一安全域的类的名字" ...其它属性... />元素在 server.xml中的嵌入位置见下表:
嵌入位置 描述 <Engine>中 <Engine>中所有虚拟主机上的所有 Web应用共享这个 Realm。例外情况是在这个<Engine>下的<Host> 或<Context>元素下还定义了自己的 Realm元素 <Host>中 <Host>下的所有 Web应用共享这个 Realm。例外情况是在这个<Host>下的<Context>元素下还定义了自己的 Realm元素 <Context>中 只有<Context>元素对应的Web应用才能使用这个Realm
1.2 为Web资源设置安全约束
为Web资源设置安全约束,需要在Web应用的web.xml文件中加入<security-constraint>、<login-config>和<security-role>元素。
1.2.1 在web.xml中加入<security-constraint>元素
在<security-constraint>元素中指定受保护的 Web资源以及所有可以访问该 Web资源的角色。例如在Tomcat的<CATALINA_HOME>/webapps/webapps/manager应用中声明了如下安全约束:
<security-constraint>
<web-resource-collection>
<web-resource-name>Text Manager interface (for scripts)</web-resource-name>
<url-pattern>/text/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>manager-script</role-name>
</auth-constraint>
</security-constraint>
以上安全约束代码指明:manager-script角色能访问 manager应用中URL入口为 “/text/*” 资源。
例如在Tomcat的<CATALINA_HOME>/webapps/webapps/examples应用中声明了如下安全约束:
<security-constraint>
<display-name>Example Security Constraint - part 1</display-name>
<web-resource-collection>
<web-resource-name>Protected Area - Allow methods</web-resource-name>
<url-pattern>/jsp/security/protected/*</url-pattern>
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>tomcat</role-name>
<role-name>role1</role-name>
</auth-constraint>
</security-constraint>
以上安全约束代码指明:只有 tomcat和role1角色能以DELETE、GET、POST或PUT方式访问examples应用中URL入口为 “/jsp/security/protected/”下的Web资源。
<security-constraint>元素的各个子元素的说明见下表:
| 属性 | 说明 |
|---|---|
| <web-resource-collection> | 声明受保护的Web资源 |
| <web-resource-name> | 标识受保护的Web资源 |
| <url-pattern> | 指定受保护的URL路径 |
| <http-method> | 指明受保护的HTTP请求方式 |
| <auth-constraint> | 声明可以访问受保护资源的角色 |
| <role-name> | 指明可以访问受保护资源的角色 |
1.2.2 在web.xml中加入<login-config>元素
在web.xml文件中再加入<login-config>元素,它指定当Web客户访问受保护的Web资源时,浏览器弹出的登录对话框类型。
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>HelloApp realm</realm-name>
</login-config>
<login-config>元素的各个子元素的说明见下表:
| 属性 | 说明 |
|---|---|
| <auth-method> | 指定验证方法,它有3个可选值:BASIC(基本验证),DIGEST(摘要验证),FORM(基于表单的验证) |
| <realm-name> | 指定安全域的名称 |
| <form-login-config> | 当验证方法为FORM时,配置验证网页和出错网页 |
| <form-login-page> | 当验证方法为FORM时,配置验证网页 |
| <form-error-page> | 当验证方法为FORM时,配置出错网页 |
基本验证
如果Web应用采用基本验证,那么当客户访问受保护的资源时,浏览器会先弹出一个对话框,要求用户输入用户名和口令。当三次尝试失败后,会显示一个错误消息网页。在网络上传送的用户名和口令数据采用 Base64编码(全是可读文本),因此这种验证方法不是非常安全。
摘要验证
摘要验证方法和基本验证的区别在于:前者不会在网络中直接传输用户的口令,而是首先采用 MD5对用户的口令进行加密,然后传输加密后的数据,这种验证方法显然更为安全。
基于表单的验证
基于表单的验证方法和基本验证方法的区别在于:前者使用自定义的登录页面来代替标准的登录对话框。
用户自定义的验证网页中必须提供一个登录表单,表单中和用户名对应的文本框必须命名为 j_username,和口令对应的文本框必须命名为 j_password,并且表单的 action值必须为 j_security_check。如下usercheck.jsp:
<html> <head> <title>Login Page for helloapp</title> </head> <body bgcolor="white"> <form method="POST" action=j_security_check> <table border="0" cellspacing="5"> <tr> <th align="right">Username:</th> <td align="left"><input type="text" name="j_username"></td> </tr> <tr> <th align="right">Password:</th> <td align="left"><input type="password" name="j_password"></td> </tr> <tr> <td align="right"><input type="submit" value="Log In"></td> <td align="left"><input type="reset" value="reset"></td> </tr> </table> </form> </body> </html>错误处理页面error.jsp如下:
<!--设置中文输出--> <%@ page contentType="text/html; charset=GB2312" %> <html><head><title>Error Page</title></head> <body> <p> 请输入合法的用户名和口令 </p> </body></html>web.xml配置如下:
<login-config> <auth-method>FORM</auth-method> <realm-name>HelloApp realm</realm-name> <form-login-config> <form-login-page>/usercheck.jsp</form-login-page> <form-error-page>/error.jsp</form-error-page> </form-login-config> </login-config>
1.2.3 在web.xml中加入<security-role>元素
最后,应该在web.xml中加入<security-role>元素,指明这个 Web应用引用的所有角色名字。例如在Tomcat的<CATALINA_HOME>/webapps/webapps/manager应用中声明引用了 manager-script 角色:
<security-role>
<description>
The role that is required to access the text Manager pages
</description>
<role-name>manager-script</role-name>
</security-role>
1.3 内存域
内存域是由 org.apache.catalina.realm.MemoryRealm 类来实现。MemoryRealm 类从一个XML文件中读取用户信息。默认情况下,该XML文件为<CATALINA_HOME>/conf/tomcat-users.xml。以下是tomcat-users.xml文件中的代码:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users><!--设定用户信息-->
<role rolename="manager-gui"/><!--定义角色-->
<role rolename="manager-script"/>
<user username="tomcat" password="tomcat" roles="manager-gui,manager-script"/>
</tomcat-users>
在以上 XML文件中,定义了两个角色和一个名为“tomcat”的用户(包括用户名和口令),该用户拥有 manager-gui 和 manager-script 角色。因此 Web客户能以 “tomcat” 用户的身份访问这两个角色中的相关资源。
接下来在应用的 META-INF/context.xml文件的<Context>元素内加上如下<Realm>元素:
<!-- Memory Realm -->
<Realm className="org.apache.catalina.realm.MemoryRealm" />
下面总结配置 MemoryRealm的步骤:
(1)按上面的步骤在你的应用程序的web.xml中配置安全约束,可以采用三种验证(Basic、Digest、基于表单Form)中的任一种。
(2)在<CATALINA_HOME>/conf/tomcat-user.xml 文件中定义用户、角色以及两者的映射关系。
(3)在server.xml 中配置<Realm>元素,指明使用的域。
1.4 JDBC域
JDBC Realm 通过 JDBC驱动程序访问存放在关系型数据库中的安全验证信息。JDBC域使得安全配置非常灵活。当修改了数据库中的安全认证信息后,不必重启tomcat服务器(两者是相互独立的)。
当用户第一次访问受保护的资源时,Tomcat将调用Realm 的authenticate()方法,该方法从数据库中读取最新的安全认证信息。该用户通过认证之后,在用户访问Web资源期间,用户的各种验证信息被保存在缓存中。
1.4.1 用户数据库的结构
创建数据库和创建两张表: users(定义用户信息,包括用户名和口令)和 users_roles (定义用户和角色的映射关系)。
1.4.2 配置<Realm>元素
将所用的数据库驱动程序拷贝到Tomcat安装目录下/lib 中,,在 server.xml中加入如下<Realm>元素
<!-- JDBC Realm -->
<Realm className="org.apache.catalina.realm.JDBCRealm"
driverName="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost/tomcatusers?useSSL=false"
connectionName="dbuser" connectionPassword="1234"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" />
<Realm>元素的各个属性说明见下表:
| 属性 | 描述 |
|---|---|
| className | 此Realm实现的类名,对JDBC而言,必须是org.apache.catalina.realm.JDBCRealm |
| connectionName | 来建立JDBC连接数据的用户名 |
| connectionPassword | 用来建立JDBC连接的数据库密码 |
| connectionURL | 用来建立JDBC连接的数据库URL |
| digest | 设定储存口令时的加密方式(SHA、MD2或只有MD5) |
| driverName | JDBC驱动程序类名 |
| roleNameCol | 在 user_role 表中代表角色的字段名 |
| userNameCol | 在 user 和 user_roles 表中代表用户名字的字段名 |
| userCredCol | 在 user 表中代表用户口令的字段名 |
| userRoleTable | 指定用户与角色映射关系的表 |
| userTable | 指定用户名 |
1.5 DataSource域(数据源域)
DataSource域和JDBCRealm 域的两者的不同是访问数据库的方式不一样:DataSourceRealm通过
JNDI DataSource 来访问数据库;而JDBCRealm通过JDBC 驱动程序来访问数据库。
DataSource域配置步骤︰
(1)按上面的步骤在你的应用程序的web.xml 中配置安全约束,可以采用三种验证(Basic、Digest、Form)中的任一种。
(2)创建数据库和创建两张表:users(定义用户信息,包括用户名和口令)和 users_roles(定义用户和角色的映射关系)。
(3)将所用的数据库驱动程序拷贝到Tomcat安装目录下/lib 中。
(4)数据源的配置
数据源的配置涉及修改server.xml和web.xml文件。创建一个名为 jdbc/tomcatusers 的 DataSource,需要在server.xml中<GlobalNamingResources>元素下加入<Resource>元素:
<GlobalNamingResources>
......
<Resource name="jdbc/tomcatusers" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="dbuser" password="1234"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/tomcatusers?autoReconnect=true&useSSL=false"/>
</GlobalNamingResources>
<!-- DataSource Realm -->
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/tomcatusers"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name"/>
为DataSourceRealm配置DataSource,需要注意以下几点:
- 在server.xml文件中已经存在<GlobalNamingResources>元素,它用于配置Tomcat服务器范围内的JNDI资源。
- 在Web应用中并不会访问这个DataSource,所以无须在web.xml中加入<resource-ref>元素,声明对DataSource的引用。
1.6 在Web应用中访问用户信息
当通过验证的用户访问Web资源时,HttpServletRequest对象的 getRemoteUser()方法可以返回访问当前网页的用户的名字。