J2EE Form-based Authentication
在Web Application中实现安全认证对系统架构分析员以及Web Application开发者来说是一个具有强制性的事务。在J2EE中,目前几乎所有的Web Container都遵循J2EE Specification,提供了各自内置的安全机制。
Web Application安全模块中有两个主要的组件:authentication 和 authorization。目前的主流Web Container一般都基于J2EE的Web Container提供了三种authentication机制:basic、form-based、mutual authentication。而大多数Web应用都使用form-based认证机制,这是由于这种机制允许Application定制自己的用户接口。而另一方面,Web Container使用在web应用中的配置描述符定义好了的安全角色来实现authorization。
通常情况下,我们使用form-based认证机制过程中会遇到三个比较常见的问题:
1、form-based认证是如何和其他安全领域(如:数据库,LDAP)协同工作的?
2、怎样在web应用系统中的配置描述文件中添加或者删除一个安全角色?
3、通常Web Container在Web资源级别强制执行角色机制,但是往往我们需要的是在一个单独的Web资源中的功能级别实现角色机制,为不同的功能分配不同的角色。
尽管目前有很多关于form-based 认证的文档以及大量的示例代码,但是几乎没有一个能够很好的解释以上三个问题,因此,实际的应用中,大多应用系统使用自己的方法来实现安全机制。
这篇文章解释了form-based认证是如何和其他安全领域协同工作的,特别是数据库。同样也解释了Web Container是如何使用安全角色来实现角色的派分,以及应用系统如何从这些安全角色中继承并保护单独Web资源中的功能。Form-Based Authentication
Form-Based 认证使开发人员可以定制自己的用户认证接口。web.xml中的login-config元素定义了认证机制的类型,包括login、errorpage的URI。
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/fail_login.html</form-error-page>
</form-login-config>
</login-config>
而在相应的登陆界面中的form表单必须包含允许登陆人录入username、password的表单。这些表单必须被分别命名为j_username  和 j_password,这些表单的值当用户点击提交按钮后被提交到j_security_check中。下面的例子显示了怎样在HTML页面中编写上述场景。
<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="password" name="j_password">
</form>
这个认证的form使用base64编码。除非使用SSL,否则很容易暴露用户名和口令。
当被保护起来的Web资源被访问的时候,Web Container就会激活这项资源的已经被配置好了的认证机制。
在Web Container实现Web应用系统安全认证的过程中,通常会执行以下几个步骤:
1、当被保护起来的Web资源被访问的时候决定该用户是否已经通过安全认证了;
2、如果用户没有通过安全认证,用户提交的请求将被重新导向到登陆界面(在Web应用系统的配置描述文件中定义)
3、依据Container中配置的安全区域验证用户是否满足该资源所要求的信任度
4、检测通过安全验证之后的用户是否被赋予了访问此项资源的角色(在Web应用系统的配置描述文件中定义)
和Basic认证机制一样,在Web应用系统配置描述文件中,form-based认证并不指定安全领域,换句话说,它并不显式的声明被其用来验证一个用户的安全领域的类型。这导致了一些混乱,例如Web Container使用什么样的安全领域去验证一个用户呢?
目前的主流Web Container验证一个用户时通常执行一下操作:
1、检测Container的安全领域是否被配置
2、使用安全领域去执行认证操作
由于数据库和LDAP在维护数据方面提供了非常灵活的实现机制,所以大多数组织可以通过他们保存认证以及授权信息。
通常Web Container都会提供三种不同的安全领域:数据库、LDAP、custom realm
例如,在TOMCAT中,server.xml配置了数据库方式的实现作为他的安全领域:
<Realm 
className="org.apache.catalina.realm.JDBCRealm" 
debug="99"
driverName="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@{IPAddress}:{Port}:{Servicename}"
         connectionName="{DB Username}"
         connectionPassword="{Password}"
         userTable="users" 
userNameCol="username" 
         userCredCol="password"
userRoleTable="user_roles" 
roleNameCol="rolename" 
/>
TOMCAT的server.xml中的<Realm>标签定义了Container将会被用做认证一个用户的安全领域的类型。需要注意的是上面的例子是实现的form-based认证机制。
注意:请参考本文结尾处的关于TOMCAT和WebLogic配置数据库安全领域的片断。
Authorization
一旦用户被验证,Container将得到被验证了的用户的安全角色并且检查其是否存在于定义在配置描述文件的<auth-constraint>标签中。
Web应用系统的配置描述符(web.xml)中的<security-constraint> 标签定义被包含的Web资源以及允许访问该资源的一系列角色。
<security-constraint>
<web-resource-collection>
<web-resource-name>AdminPages</web-resource-name>
<description> accessible by authorised users </description>
<url-pattern>/admin/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<description>These are the roles who have access</description>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
一般来说Web Container在页面级别强制执行角色授权,但是,商业应用系统同样可以定义在一个页面中强制执行功能性授权。当然这需要在应用系统中添加一些自己特定的安全角色。
由于授权角色是动态的,开发人员可能会在哪些安全角色需要被添加到配置描述文件中这个问题上感觉有些混乱。通常我们的做法是,定义一个比较高的级别的角色,然后让所有的用户都继承这个角色(这样做的目的是为了能让所有的用户都能访问基本的Web资源)。另外我们要做的是对Web资源中的特定的功能定义一些比较低级别的角色,这些角色不需要被配置在应用系统的配置描述符中,因为应用系统已经配置了包含这些用户的高级别的角色。这允许Web应用系统利用容器支持的授权机制并实现特定应用系统的授权。
例如,你可以在配置描述文件中为所有的用户定义一个高级别的Administrator角色保护Web资源,这将允许属于这个角色的所有用户访问管理页面。为了派分管理页面中的功能,你可以另外创建例如sysadmin或者appadmin这些角色。应用系统可以从这些安全角色上继承,得到他们需要的授权。因此,应用系统可以使用这些继承到的授权去控制对特定功能的访问。
尽管系统的特定安全角色没有被定义在其配置描述文件中,但是这些角色仍然可以被request对象的isUserInRole方法来检测是否通过了授权。Advantages
通过对authentication 和 authorization的介绍,我们已经知道,他们具有的如下几点优势:
1、Web应用不用自己去实现安全认证机制。在Container中配置他们是一件很简单的事情。
2、Web应用可以通过getRemoteUser, IsUserInRole, 和getUserPrincipal得到需要的安全验证结果。
3、Web Container有能力对EJB Container做同样的认证
Configuring the Database Security Realm in Tomcat
1、创建User表
这个表有两个必须的字段username和password
create table users (username varchar(20) not null, password(20) not null)
2、创建Role表
这个表维护应用系统中的角色列表,它只需要一个role字段
create table roles (rolename varchar(20) not null)
3、创建User-Role关联表
这个表维护在一个user和一个role之间的关系,一个user可以属于一个或多个角色
create table user_roles (username varchar(20) not null, rolename varchar(20) not null) 
4、插入数据到表中
insert into users values('user1', 'password') insert into role values('manager')
insert into user_roles values('user1', 'manager')
5、配置TOMCAT(该示例需要Oracle的thin driver)。
打开{tomcat}\conf\目录下的server.xml文件,找到memory realm段落,注释它,然后将下面的短路拷贝到文件中
<Realm 
className="org.apache.catalina.realm.JDBCRealm" 
         debug="99"
driverName="oracle.jdbc.driver.OracleDriver" 
connectionURL="jdbc:oracle:thin:@{IP address}:{Port}:{Servicename}"
connectionName="{DB Username}"
         connectionPassword="{Password}"
         userTable="users" 
         userNameCol="username" 
         userCredCol="password"
         userRoleTable="user_roles" 
         roleNameCol="rolename" 
/>
替换上文中的环境值:
{IP Address} - 数据库服务器的IP地址 {Port} - 数据库端口号 {Servicename} - 数据库服务名 {DB Username} - 数据库登陆名 {Password} - 数据库登陆名对应的口令

注意:你需要得到你使用的数据库的JDBC驱动
6、拷贝Oracle的thin driver的JAR文件oracle_thin_driver.jar到{tomcat_home}/server/lib目录下(如果你使用了不同的数据库,请向ISV索取所需要的JDBC Driver。
7、配置Web应用系统的配置描述文件
将下面的段落拷贝到你的配置描述文件中,这里我们使用的是tomcat的web.xml
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/*</url-pattern>
<http-method>DELETE</http-method>
<http-method>GET</http-method