上一级 首页 下一级

架构设计

实现一个完整的用户安全管理框架,可以有两种实现途径。在J2EE出现以前,大部分是由应用系统本身实现。因此,很多有经验的软件商都拥有自己成熟的用户安全管理系统,但是缺点也是比较明显,自己设计的用户安全管理系统可重用性低,有的和具体应用程序过分紧密地绑定在一起,无法移植到其他系统上。

但是,随着业务量上升,应用系统的不断增加,会出现不同应用系统拥有不同的用户登录验证体系,很显然,这给用户访问带来了不方便,用户不可能为每个系统注册一套用户和密码,定制一套用户角色。因此,整个服务器群中需要统一的用户权限验证体系。而J2EE容器的用户安全管理框架再辅助以LDAP或数据库系统,可以很方便地达到这个目标。


2.1  角色

J2EE容器的用户安全框架是基于RBACRoled-Based Access Control,相关网址:http://csrc.nist.gov/rbac/)设计模型建立的,这是一个基于角色的访问权限控制模型。

首先必须了解角色的含义,在RBAC中角色Role的定义是:Role是明确表达访问控制(Aceess Control)策略的一种语义构建词。

角色可以是指做某些事情的资格,比如医生或物理学家;也可以包含权力和责任的意思,如部门经理或局长等。角色和组(groups)是有区别的。组就是纯粹代表一群用户;角色一方面代表一系列用户,另外一方面可以代表一系列权限,因此可以说是用户和权限的结合体。

引入角色的概念主要是为了分离用户和访问权限的直接联系。用户与访问权限的直接组合可能是短暂的,而角色则可以相对稳定,因为一个系统中和角色相关的权限变化是有限的。

RBAC理论出现之前,很多人都是把用户和权限混淆在一起,这样当用户或权限发生变化时,都会涉及到对方,很显然这在实际实现中将是非常复杂的。所以诞生RBAC,创造了一个“角色”的名词,注意这是人为创造的语义词。角色就是用户和权限之间的第3者,通过引入角色概念,将用户和权限的关系解耦。这样用户的变化只要涉及到角色就可以,无需考虑权限。而权限的变化只涉及到角色,无需考虑用户或用户组。

因此,基于角色的访问控制系统可以分为两个部分:与角色相关的访问权限系统以及与角色相关的用户管理系统。这样,通过角色这个中间者,将用户和权限联系在一起。其实这也非常符合日常生活的逻辑,例如王三来到某公司做业务员,公司章程规定了业务员一定的权限和职责,王三进入了业务员的角色,王三也就有了这些权限和职责,但这些权限职责不是和王三本人有直接联系的,而是通过王三的角色才会发生在王三身上;如果王三升迁做经理,表示其进入经理这样的角色,由此经理角色拥有的权限和职责王三又会拥有。

由于有了这样两个分离的系统,因此在具体应用上可以分别实现,在J2EE中,与角色相关的访问权限是通过配置文件(Web.xmlejb-jar.xml)由容器自动实现的,而且这种访问权限的配置也是非常方便灵活的。

而与角色相关的用户系统则由具体应用系统的开发者来实现,可以采取基于数据库或LDAP等技术的数据系统来实现,例如用户注册资料的新增和修改等。

本项目的设计思路就是完全按照这两种分离的思路实现的,将与角色相关的访问权限系统交由J2EE容器实现。因此,如何配置J2EE将是本项目实现中的一个主要部分;代码设计编程则主要集中在基于数据库的用户管理系统上。

2.2  J2EEJAAS

J2EE容器实现了与角色相关的访问权限功能,如何在自己的具体应用系统开发中使用J2EE的安全系统?

通过使用J2EE容器提供的JAAS Java Authentication Authorization ServiceJava验证和授权API),JAASJ2EE服务器用来帮助应用系统实现安全功能的。当应用系统的开发者具体实现了LoginModule API,那么J2EE容器就执行LoginModule接口,通过接口和具体实现之间的关系,J2EE容器将结合具体应用系统实现特定的JAAS功能。

标准的LoginModule接口可以让应用系统开发者自由地选择数据系统,如数据库、LDAPlightweight directory access protocol)或者共享文件系统。这些变化却无需修改程序。

这就形成了基于J2EE安全系统开发模式:编写一个Login Module,然后打包到自己的具体应用中,然后以一种约定的方式发布到J2EE平台上。

6-1显示在用户登录的情况下,应用系统实现的LoginModuleJ2EE容器如何交互实现JAAS

6-1  J2EELogin交互图

当用户实现LoginContext.login()时,Web容器将调用JAASJAAS确认应用系统自己实现的LoginModule正确配置后,用户和密码验证等验证工作交由应用系统自己的LoginModule实现。

正常登录后,用户将通过Web容器访问业务逻辑核心的EJB容器,J2EE中可以对每个EJB的方法实现访问权限控制,这些都可以在ejb-jar.xml中配置。当Web容器发出一个EJBlookup命令时,容器将从subject中获得角色。然后和ejb-jar.xml配置中相应EJB允许操作的角色相比较,如果两者一致,则允许lookup命令执行,EJB层将返回lookup后的bean

在本项目中,由于采取JBoss作为J2EE容器,JBoss提供了绑定的LoginModule,如LdapLoginModule DatabaseServerLoginModul,因此本项目就不必再编写LoginModule,只要在Jboss的配置文件中配置相应的LoginModule就可以。

关于LoginContext的实现有多种途径。

第一种是应用系统自己通过调用LoginContext.login()实现拦截验证功能,在拦截功能的模块中包含如下语句:

AppCallbackHandler handler = new AppCallbackHandler(name, passwordChar);

LoginContext lc = new LoginContext(Constants.LOGIN_MODULE, handler);

lc.login();

Subject subject = lc.getSubject();

PrivilegedAction action = new PrivilegedAction () {

public Object run() {

     // do something

     }

};

// perform action as Subject t

Subject.doAs(subject, action);

拦截功能的实现有几种,使用Servlet 2.3以上版本支持的Filter是比较好的办法,还需要重载HTTPServletRequestWrapper的某些方法。

这样做的好处是可以在用户登录后加入一些应用系统需要实现的预先工作,定制性很强,甚至直接在应用系统中使用自己的LoginModule,但是这些工作实现起来比较麻烦。

另外一种则比较简单,只要在Web按照Servlet 2.3的安全章节所规定的,在Web.xml配置login-config,由容器自动生成LoginContext并保存返回的Subject。本项目将采取该方案,关于login-config的配置和J2EE登录配置见2.3节。

2.3  单点登录

单点登录SSOsingle sign on是指在分布式环境下整个系统只有一个可以登录进入的点它对所有的请求Request都是通用的。单点登录可以保证用户能够访问到可以访问的资源,如果有一个未被授权的请求要求访问被保护的资源,这个请求将自动被导向到相应的验证点进行登录验证。

J2EE容器支持单点登录模式的实现。通过使用J2EE Web层安全机制,可以保护Web层的一些资源如URLURL模型以及HTTP的提交方式(POSTGET等)。当未被授权的用户访问这些受保护的资源时,J2EE容器会自动将用户导向到规定的登录界面,要求用户输入用户名和密码。

J2EE容器支持常见的下列几种登录验证机制。

·          基于HTTP的基本验证(HTTP Based Authentication):这是基于HTTP/1.0规定中的用户名和密码验证机制。类似以前在Apache中的设置,该方式的缺点是不够安全,密码只是简单地使用base64编码。当用户访问被保护资源时,浏览器跳出一个提示框,要求输入用户名和密码,如图6-2所示。

·          基于HTTPS的客户端验证(HTTPS Client Authentication0):客户端通过HTTPS HTTP over SSL)和服务器端发生互动,这个机制需要公用密钥证书,因此要安全得多。

·          基于表单验证(form-based authentication):登录界面可以使用JSP/HTML特定定制,因此在界面上可以更加美观,如图6-3所示。

      

6-2  基于HTTP/1.0的登录界面                     6-3  登录表单

但是对登录界面表单有限制。例如login.jsp是用户登录的界面,那么在login.jsp中需要如下语句:

<form method="POST" action="j_security_check">

<input type="text" name="j_username">

<input type="password" name="j_password">

</form>

其中,Action的值、用户名以及密码名都必须分别严格采用j_security_checkj_usernamej_password英文写法。除此之外可以任由开发人员发挥了。

Login.jsp提交到j_security_check后,Web容器将执行,如图6-1所示的流程,如果验证成功,返回Web层的一个subject

登录类型的设置比较简单,在web.xml中加入如下语句:

  <login-config>

    <auth-method>BASIC</auth-method>

    <realm-name> Register User </realm-name>

  </login-config>

这表示是基于HTTP的基本验证,而使用下列语句表示基于表单的验证:

<login-config>

    <auth-method>FORM</auth-method>

    <realm-name>SecurityRealm</realm-name>

    <form-login-config>

      <form-login-page>/account/login.jsp</form-login-page>

      <form-error-page>/account/login_error.jsp</form-error-page>

    </form-login-config>

 </login-config>

需要应用系统开发人员自己设置login的页面和出错页面,/account/login.jsp表示在路径accountlogin.jsp页面。

2.4  邮件发送组件

在本项目用户管理的需求中,需要对丢失的密码进行查询。输入用户的信箱,系统将查询后的密码发往用户的信箱。这将使用JavaMail连接专门的SMTP服务器进行信件发送,这个信件的发送过程有可能因为网络原因或其他未知原因导致处理时间延长,如果系统中的其他处理事务都要等待这个过程的完成,显然是没有效率而且问题会很多。

采用JMS的邮件发送组件是基于一个异步消息机制的可重用系统,该组件系统采取session bean作为Queue的消息生产者,而MDB作为Queue的消息使用者,作为EJB的一个实现,可以重复使用在需要邮件发送功能的应用系统中,如图6-4所示。

6-4  邮件发送组件图

客户端只要直接调用EJB AsyncSender就可以实现邮件发送功能。


上一级 首页 下一级