颠覆软件

关注 : 架构与设计,敏捷,快速开发,项目管理,执行力,SSH,RoR

Archive for October, 2006

解决response.sendRedirect(“mypage.jsp”)的问题

October 31, 2006

key word: response.sendRedirect() 页面转向

在jsp里经常要碰到页面的跳转,但是有一个问题,就是跳转的时候有些信息无法携带,比如
response.sendRedirect(“myPage.jsp?method=save”)通过URL携带信息没问题,但是request.setAttribute(“message”,myObject);就无法携带. 以前碰到这个问题我都回避了,改用session存放信息,但是显然不足取。

察看了一下,在servlet里的途径好办,jsp里必须调用<jsp:forward>标签

<jsp:forward page=myPage.jsp/>

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

[zt]关于用户角色权限的一点想法

October 26, 2006

前言

权限往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断“ Who What(Which) 进行 How 的操作”的逻辑表达式是否为真。针对不同的应用,需要根据项目的实际情况和具体架构,在维护性、灵活性、完整性等 N 多个方案之间比较权衡,选择符合的方案。

目标

直观,因为系统最终会由最终用户来维护,权限分配的直观和容易理解,显得比较重要,系统不辞劳苦的实现了组的继承,除了功能的必须,更主要的就是因为它足够直观。

简单,包括概念数量上的简单和意义上的简单还有功能上的简单。想用一个权限系统解决所有的权限问题是不现实的。设计中将常常变化的“定制”特点比较强的部分判断为业务逻辑,而将常常相同的“通用”特点比较强的部分判断为权限逻辑就是基于这样的思路。

扩展,采用可继承在扩展上的困难。的 Group 概念在支持权限以组方式定义的同时有效避免了重定义时

现状

对于在企业环境中的访问控制方法,一般有三种:

1. 自主型访问控制方法。目前在我国的大多数的信息系统中的访问控制模块中基本是借助于自主型访问控制方法中的访问控制列表 (ACLs)

2. 强制型访问控制方法。用于多层次安全级别的军事应用。

3. 基于角色的访问控制方法( RBAC )。是目前公认的解决大型企业的统一资源访问控制的有效方法。其显著的两大特征是: 1. 减小授权管理的复杂性,降低管理开销。 2. 灵活地支持企业的安全策略,并对企业的变化有很大的伸缩性。

名词

粗粒度:表示类别级,即仅考虑对象的类别 (the type of object) ,不考虑对象的某个特

定实例。比如,用户管理中,创建、删除,对所有的用户都一视同仁,并不区分操作的具体对象实例。

细粒度:表示实例级,即需要考虑具体对象的实例 (the instance of object) ,当然,细

粒度是在考虑粗粒度的对象类别之后才再考虑特定实例。比如,合同管理中,列表、删除,需要区分该合同实例是否为当前用户所创建。

原则

权限逻辑配合业务逻辑。即权限系统以为业务逻辑提供服务为目标。相当多细粒度的权限问题因其极其独特而不具通用意义,它们也能被理解为是“业务逻辑”的一部分。比如,要求:“合同资源只能被它的创建者删除,与创建者同组的用户可以修改,所有的用户能够浏览”。这既可以认为是一个细粒度的权限问题,也可以认为是一个业务逻辑问题。在这里它是业务逻辑问题,在整个权限系统的架构设计之中不予过多考虑。当然,权限系统的架构也必须要能支持这样的控制判断。或者说,系统提供足够多但不是完全的控制能力。即,设计原则归结为:“系统只提供粗粒度的权限,细粒度的权限被认为是业务逻辑的职责”。

需要再次强调的是,这里表述的权限系统仅是一个“不完全”的权限系统,即,它不提供所有关于权限的问题的解决方法。它提供一个基础,并解决那些具有“共性”的 ( 或者说粗粒度的 ) 部分。在这个基础之上,根据“业务逻辑”的独特权限需求,编码实现剩余部分 ( 或者说细粒度的 ) 部分,才算完整。回到权限的问题公式,通用的设计仅解决了 Who+What+How 的问题,其他的权限问题留给业务逻辑解决。

概念

Who :权限的拥用者或主体( Principal User Group Role Actor 等等)

What :权限针对的对象或资源( Resource Class )。

How :具体的权限( Privilege, 正向授权与负向授权)。

Role :是角色,拥有一定数量的权限。

Operator :操作。表明对 What How 操作。

说明

User Role 相关, 用户仅仅是纯粹的用户,权限是被分离出去了的。 User 是不能与 Privilege 直接相关的, User 要拥有对某种资源的权限,必须通过 Role 去关联。 解决 Who 的问题。

Resource :就是系统的资源,比如部门新闻,文档等各种可以被提供给用户访问的对象。资源可以反向包含自身,即树状结构,每一个资源节点可以与若干指定权限类别相关可定义是否将其权限应用于子节点。

Privilege :是 Resource Related 的权限。就是指,这个权限是绑定在特定的资源实例上的。比如说部门新闻的发布权限,叫做 部门新闻发布权限 。这就表明,该 Privilege 是一个发布权限,而且是针对部门新闻这种资源的一种发布权限。 Privilege 是由 Creator 在做开发时就确定的。权限,包括系统定义权限和用户自定义权限用户自定义权限之间可以指定排斥和包含关系 ( 如:读取,修改,管理三个权限,管理 权限 包含 前两种权限 ) Privilege 删除 是一个抽象的名词,当它不与任何具体的 Object Resource 绑定在一起时是没有任何意义的。拿新闻发布来说,发布是一种权限,但是只说发布它是毫无意义的。因为不知道发布可以操作的对象是什么。只有当发布与新闻结合在一起时,才会产生真正的 Privilege 。这就是 Privilege Instance 。权限系统根据需求的不同可以延伸生很多不同的版本。

Role :是粗粒度和细粒度 ( 业务逻辑 ) 的接口,一个基于粗粒度控制的权限框架软件,对外的接口应该是 Role ,具体业务实现可以直接继承或拓展丰富 Role 的内容, Role 不是如同 User Group 的具体实体,它是接口概念,抽象的通称。

Group :用户组, 权限分配的单位与载体。权限不考虑分配给特定的用户。组可以包括组 ( 以实现权限的继承 ) 组可以包含用户,组内用户继承组的权限。 Group 要实现继承。即在创建时必须要指定该 Group Parent 是什么 Group 。在粗粒度控制上,可以认为,只要某用户直接或者间接的属于某个 Group 那么它就具备这个 Group 的所有操作许可。细粒度控制上,在业务逻辑的判断中, User 仅应关注其直接属于的 Group ,用来判断是否“同组” Group 是可继承的,对于一个分级的权限实现,某个 Group 通过“继承”就已经直接获得了其父 Group 所拥有的所有“权限集合”,对这个 Group 而言,需要与权限建立直接关联的,仅是它比起其父 Group 需要“扩展”的那部分权限。子组继承父组的所有权限,规则来得更简单,同时意味着管理更容易。为了更进一步实现权限的继承,最直接的就是在 Group 上引入“父子关系”。

User Group 是多对多的关系。即一个 User 可以属于多个 Group 之中,一个 Group 可以包括多个 User 。子 Group 与父 Group 是多对一的关系。 Operator 某种意义上类似于 Resource + Privilege 概念,但这里的 Resource 仅包括 Resource Type 不表示 Resource Instance Group 可以直接映射组织结构, Role 可以直接映射组织结构中的业务角色,比

较直观,而且也足够灵活。 Role 对系统的贡献实质上就是提供了一个比较粗颗粒的分配单位。

Group Operator 是多对多的关系。各概念的关系图示如下:   

 



解释

Operator 的定义包括了 Resource Type Method 概念。即, What How 的概念。之所以将 What How 绑定在一起作为一个 Operator 概念而不是分开建模再建立关联,这是因为很多的 How 对于某 What 才有意义。比如,发布操作对新闻对象才有意义,对用户对象则没有意义。

How 本身的意义也有所不同,具体来说,对于每一个 What 可以定义 N 种操作。比如,对于合同这类对象,可以定义创建操作、提交操作、检查冲突操作等。可以认为, How 概念对应于每一个商业方法。其中,与具体用户身份相关的操作既可以定义在操作的业务逻辑之中,也可以定义在操作级别。比如,创建者的浏览视图与普通用户的浏览视图要求内容不同。既可以在外部定义两个操作方法,也可以在一个操作方法的内部根据具体逻辑进行处理。具体应用哪一种方式应依据实际情况进行处理。

这样的架构,应能在易于理解和管理的情况下,满足绝大部分粗粒度权限控制的功能需要。但是除了粗粒度权限,系统中必然还会包括无数对具体 Instance 的细粒度权限。这些问题,被留给业务逻辑来解决,这样的考虑基于以下两点:

一方面,细粒度的权限判断必须要在资源上建模权限分配的支持信息才可能得以实现。比如,如果要求创建者和普通用户看到不同的信息内容,那么,资源本身应该有其创建者的信息。另一方面,细粒度的权限常常具有相当大的业务逻辑相关性。对不同的业务逻辑,常常意味着完全不同的权限判定原则和策略。相比之下,粗粒度的权限更具通用性,将其实现为一个架构,更有重用价值;而将细粒度的权限判断实现为一个架构级别的东西就显得繁琐,而且不是那么的有必要,用定制的代码来实现就更简洁,更灵活。

所以细粒度控制应该在底层解决, Resource 在实例化的时候,必需指定 Owner GroupPrivilege 在对 Resource 进行操作时也必然会确定约束类型:究竟是 OwnerOK 还是 GroupOK 还是 AllOK Group 应和 Role 严格分离 User Group 是多对多的关系, Group 只用于对用户分类,不包含任何 Role 的意义; Role 只授予 User ,而不是 Group 。如果用户需要还没有的多种 Privilege 的组合,必须新增 Role Privilege 必须能够访问 Resource ,同时带 User 参数,这样权限控制就完备了。

思想

权限系统的核心由以下三部分构成: 1. 创造权限, 2. 分配权限, 3. 使用权限,然后,系统各部分的主要参与者对照如下: 1. 创造权限 - Creator 创造, 2. 分配权限 - Administrator 分配, 3. 使用权限 - User

1. Creator 创造 Privilege Creator 在设计和实现系统时会划分,一个子系统或称为模块,应该有哪些权限。这里完成的是 Privilege Resource 的对象声明,并没有真正将 Privilege 与具体 Resource 实例联系在一起,形成 Operator

2. Administrator 指定 Privilege Resource Instance 的关联。在这一步, 权限真正与资源实例联系到了一起, 产生了 Operator Privilege Instance )。 Administrator 利用 Operator 这个基本元素,来创造他理想中的权限模型。如,创建角色,创建用户组,给用户组分配用户,将用户组与角色关联等等 这些操作都是由 Administrator 来完成的。

3. User 使用 Administrator 分配给的权限去使用各个子系统。 Administrator 是用户,在他的心目中有一个比较适合他管理和维护的权限模型。于是,程序员只要回答一个问题,就是什么权限可以访问什么资源,也就是前面说的 Operator 。程序员提供 Operator 就意味着给系统穿上了盔甲。 Administrator 就可以按照他的意愿来建立他所希望的权限框架 可以自行增加,删除,管理 Resource Privilege 之间关系。可以自行设定用户 User 和角色 Role 的对应关系。 ( 如果将 Creator 看作是 Basic 的发明者, Administrator 就是 Basic 的使用者,他可以做一些脚本式的编程 ) Operator 是这个系统中最关键的部分,它是一个纽带,一个系在 Programmer Administrator User 之间的纽带。

用一个功能模块来举例子。

一.建立角色功能并做分配:

1 .如果现在要做一个员工管理的模块 ( Resources) ,这个模块有三个功能,分别是:增加,修改,删除。给这三个功能各自分配一个 ID ,这个 ID 叫做功能代号:

Emp_addEmp Emp_deleteEmp Emp_updateEmp

2 .建立一个角色 (Role) ,把上面的功能代码加到这个角色拥有的权限中,并保存到数据库中。角色包括系统管理员,测试人员等。

3 .建立一个员工的账号,并把一种或几种角色赋给这个员工。比如说这个员工既可以是公司管理人员,也可以是测试人员等。这样他登录到系统中将会只看到他拥有权限的那些模块。

二.把身份信息加到 Session 中。

登录时,先到数据库中查找是否存在这个员工,如果存在,再根据员工的 sn 查找员工的权限信息,把员工所有的权限信息都入到一个 Hashmap 中,比如就把上面的 Emp_addEmp 等放到这个 Hashmap 中。然后把 Hashmap 保存在一个 UserInfoBean 中。最后把这个 UserInfoBean 放到 Session 中,这样在整个程序的运行过程中,系统随时都可以取得这个用户的身份信息。

三.根据用户的权限做出不同的显示。

可以对比当前员工的权限和给这个菜单分配的“功能 ID ”判断当前用户是否有打开这个菜单的权限。 例如:如果保存员工权限的 Hashmap 中没有这三个 ID 的任何一个,那这个 单就不会显示,如果 员工 Hashmap 中有任何一个 ID ,那这个 单都会显示。

对于一个新闻系统 (Resouce) ,假设它有这样的功能 (Privilege) :查看,发布,删除,修改;假设对于删除,有 新闻系统管理者只能删除一月前发布的,而超级管理员可删除所有的这样的限制,这属于业务逻辑 (Business logic) ,而不属于用户权限范围。也就是说权限负责有没有删除的 Permission ,至于能删除哪些内容应该根据 UserRole or UserGroup 来决定 ( 当然给 UserRole or UserGroup 分配权限时就应该包含上面两条业务逻辑 )

一个用户可以拥有多种角色,但同一时刻用户只能用一种角色进入系统。角色的划分方法可以根据实际情况划分,按部门或机构进行划分的,至于角色拥有多少权限,这就看系统管理员赋给他多少的权限了。用户—角色—权限的关键是角色。用户登录时是以用户和角色两种属性进行登录的(因为一个用户可以拥有多种角色,但同一时刻只能扮演一种角色),根据角色得到用户的权限,登录后进行初始化。这其中的技巧是同一时刻某一用户只能用一种角色进行登录。

针对不同的“角色”动态的建立不同的组, 每个项目建立一个单独的 Group ,对于新的项目,建立新的 Group 即可。在权限判断部分,应在商业方法上予以控制。比如:不同用户的“操作能力”是不同的 ( 粗粒度的控制应能满足要求 ) ,不同用户的“可视区域”是不同的 ( 体现在对被操作的对象的权限数据,是否允许当前用户访问,这需要对业务数据建模的时候考虑权限控制需要 )

扩展性:

有了用户 / 权限管理的基本框架, Who(User/Group) 的概念是不会经常需要扩展的。变化的可能是系统中引入新的 What ( 新的 Resource 类型 ) 或者新的 How( 新的操作方式 ) 。那在三个基本概念中,仅在 Permission 上进行扩展是不够的。这样的设计中 Permission 实质上解决了 How 的问题,即表示了“怎样”的操作。那么这个“怎样”是在哪一个层次上的定义呢?将 Permission 定义在“商业方法”级别比较合适。比如,发布、购买、取消。每一个商业方法可以意味着用户进行的一个“动作”。定义在商业逻辑的层次上,一方面保证了数据访问代码的“纯洁性”,另一方面在功能上也是“足够”的。也就是说,对更低层次,能自由的访问数据,对更高层次,也能比较精细的控制权限。

确定了 Permission 定义的合适层次,更进一步,能够发现 Permission 实际上还隐含了 What 的概念。也就是说,对于 What How 操作才会是一个完整的 Operator 。比如,“发布”操作,隐含了“信息”的“发布”概念,而对于“商品”而言发布操作是没有意义的。同样的,“购买”操作,隐含了“商品”的“购买”概念。这里的绑定还体现在大量通用的同名的操作上,比如,需要区分“商品的删除”与“信息的删除”这两个同名为“删除”的不同操作。

提供权限系统的扩展能力是在 Operator (Resource + Permission) 的概念上进行扩展。 Proxy 模式是一个非常合适的实现方式。实现大致如下:在业务逻辑层 (EJB Session Facade [Stateful SessionBean] ) ,取得该商业方法的 Methodname ,再根据 Classname Methodname 检索 Operator 数据,然后依据这个 Operator 信息和 Stateful 中保存的 User 信息判断当前用户是否具备该方法的操作权限。

应用在 EJB 模式下,可以定义一个很明确的 Business 层次,而一个 Business 可能意味着不同的视图,当多个视图都对应于一个业务逻辑的时候,比如, Swing Client 以及 Jsp Client 访问的是同一个 EJB 实现的 Business 。在 Business 层上应用权限较能提供集中的控制能力。实际上,如果权限系统提供了查询能力,那么会发现,在视图层次已经可以不去理解权限,它只需要根据查询结果控制界面就可以了。

灵活性

Group Role ,只是一种辅助实现的手段,不是必需的。如果系统的 Role 很多,逐个授权违背了“简单,方便”的目的,那就引入 Group ,将权限相同的 Role 组成一个 Group 进行集中授权。 Role 也一样,是某一类 Operator 的集合,是为了简化针对多个 Operator 的操作。

Role 把具体的用户和组从权限中解放出来。一个用户可以承担不同的角色,从而实现授权的灵活性。当然, Group 也可以实现类似的功能。但实际业务中, Group 划分多以行政组织结构或业务功能划分;如果为了权限管理强行将一个用户加入不同的组,会导致管理的复杂性。

Domain 的应用。为了授权更灵活,可以将 Where 或者 Scope 抽象出来,称之为 Domain ,真正的授权是在 Domain 的范围内进行,具体的 Resource 将分属于不同的 Domain 。比如:一个新闻机构有国内与国外两大分支,两大分支内又都有不同的资源(体育类、生活类、时事政治类)。假如所有国内新闻的权限规则都是一样的,所有国外新闻的权限规则也相同。则可以建立两个域,分别授权,然后只要将各类新闻与不同的域关联,受域上的权限控制,从而使之简化。

权限系统还应该考虑将功能性的授权与资源性的授权分开。很多系统都只有对系统中的数据(资源)的维护有权限控制,但没有对系统功能的权限控制。

权限系统最好是可以分层管理而不是集中管理。大多客户希望不同的部门能且仅能管理其部门内部的事务,而不是什么都需要一个集中的 Administrator Administrators 组来管理。虽然你可以将不同部门的人都加入 Administrators 组,但他们的权限过大,可以管理整个系统资源而不是该部门资源。

正向授权与负向授权:正向授权在开始时假定主体没有任何权限,然后根据需要授予权限,适合于权限要求严格的系统。负向授权在开始时假定主体有所有权限,然后将某些特殊权限收回。

权限计算策略:系统中 User Group Role 都可以授权,权限可以有正负向之分,在计算用户的净权限时定义一套策略。

系统中应该有一个集中管理权限的 AccessService ,负责权限的维护(业务管理员、安全管理模块)与使用(最终用户、各功能模块),该 AccessService 在实现时要同时考虑一般权限与特殊权限。虽然在具体实现上可以有很多,比如用 Proxy 模式,但应该使这些 Proxy 依赖于 AccessService 。各模块功能中调用 AccessService 来检查是否有相应的权限。所以说,权限管理不是安全管理模块自己一个人的事情,而是与系统各功能模块都有关系。每个功能模块的开发人员都应该熟悉安全管理模块,当然,也要从业务上熟悉本模块的安全规则。

技术实现

1 .表单式认证,这是常用的,但用户到达一个不被授权访问的资源时, Web 容器就发

出一个 html 页面,要求输入用户名和密码。

2 .一个基于 Servlet Sign in/Sign out 来集中处理所有的 Request ,缺点是必须由应用程序自己来处理。

3 .用 Filter 防止用户访问一些未被授权的资源, Filter 会截取所有 Request/Response

然后放置一个验证通过的标识在用户的 Session 中,然后 Filter 每次依靠这个标识来决定是否放行 Response

这个模式分为:

Gatekeeper :采取 Filter 或统一 Servlet 的方式。

Authenticator Web 中使用 JAAS 自己来实现。

用户资格存储 LDAP 或数据库:

1.       Gatekeeper 拦截检查每个到达受保护的资源。首先检查这个用户是否有已经创建

好的 Login Session ,如果没有, Gatekeeper 检查是否有一个全局的和 Authenticator 相关的 session

2.       如果没有全局的 session ,这个用户被导向到 Authenticator Sign-on 页面,

要求提供用户名和密码。

3.       Authenticator 接受用户名和密码,通过用户的资格系统验证用户。

4.       如果验证成功, Authenticator 将创建一个全局 Login session ,并且导向 Gatekeeper

来为这个用户在他的 web 应用中创建一个 Login Session

5.    Authenticator Gatekeepers 联合分享 Cookie ,或者使用 Tokens Query 字符里。

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

[zt]RBAC 模型初探

October 26, 2006

访问控制背景

访问控制技术是由美国国防部(Department of Defense, DoD)资助的研究和开发成果演变而来的。这一研究导致两种基本类型访问控制的产生:自主访问控制(Discretionary Access Control, DAC)和强制访问控制(Mandatory Access Control, MAC)。最初的研究和应用主要是为了防止机密信息被未经授权者访问,近期的应用主要是把这些策略应用到为商业领域。

自主访问控制,允许把访问控制权的授予和取消留给个体用户来判断。为没有访问控制权的个体用户授予和废除许可。自主访问控制机制允许用户被授权和
取消访问其控制之下的任何客体(object),换句话说,用户就是他们控制下的客体的拥有者。然而,对于多数组织来说,最终用户对所访问的信息没有拥有权。对于这些组织,公司或代理机构是事实上的系统客体和处理他们的程序的拥有者。访问优先权受组织控制,而且也常常基于雇员功能而不是数据所有权。

强制访问控制,在美国国防部 Trusted Computer Security Evaluation Criteria (TCSEC) 中定义如下:“一种限制访问客体的手段,它以包含在这些客体中的信息敏感性和访问这些敏感性信息的主体的正式授权信息(如清除)为基础”。

以上访问控制策略对于处理一些无需保密但又敏感的信息的政府和行业组织的需求并不是特别的适合。在这样的环境下,安全目标支持产生于现有法律、道德规范、规章、或一般惯例的高端组织策略。这些环境通常需要控制个体行为的能力,而不仅仅是如何根据信息的敏感性为其设置标签从而访问这一信息的个人能力。

什么是基于角色访问控制(Role-Based Access Control, RBAC)?NIST 有如下定义。

访问是一种利用计算机资源去做某件事情的的能力,访问控制是一种手段,通过它这种能力在某些情况下被允许或者受限制(通常是通过物理上和基于系统的控制)。基于计算机的访问控制不仅可规定是“谁”或某个操作有权使用特定系统资源,而且也能规定被允许的访问类型。这些控制方式可在计算机系统或者外部设备中实现。

就基于角色访问控制而言,访问决策是基于角色的,个体用户是某个组织的一部分。用户具有指派的角色(比如医生、护士、出纳、经理)。定义角色的过程应该基于对组织运转的彻底分析,应该包括来自一个组织中更广范围用户的输入。

访问权按角色名分组,资源的使用受限于授权给假定关联角色的个体。例如,在一个医院系统中,医生角色可能包括进行诊断、开据处方、指示实验室化验等;而研究员的角色则被限制在收集用于研究的匿名临床信息工作上。

控制访问角色的运用可能是一种开发和加强企业特殊安全策略,进行安全管理过程流程化的有效手段。

用户(User)和角色(Role)

用户指访问系统中的资源的主体,一般为人,也可为 Agent 等智能程序。角色指应用领域内一种权力和责任的语义综合体,可以是一个抽象概念,也可以是对应于实际系统中的特定语义体,比如组织内部的职务等。针对角色属性的不同,某些模型中将角色进一步细分为普通角色和管理员角色(可理解为全局角色)。

许可(Permissions)和权限(Permission)

许可描述了角色对计算机资源的访问和操作所具有的权限,其反映的是授权的结果。比如授予某个角色对计算机资源有读的权限,则代表了一个许可的存在,这个许可表示:角色获取了对计算机资源的读许可。针对操作来说,其描述的是许可和操作之间的一种关联关系,而这层关系则表示了某一角色对某一操作所具有的权限及权限状态。

角色和指派(Assignment)

指派包含两个方面,用户指派和许可指派。用户指派表示的是,将用户指派给特定的角色。许可指派表示的是为角色指派计算机资源的访问和操作许可。

会话(session)

会话表示的是用户和角色之间的关系。用户每次必须通过建立会话来激活角色,得到相应的访问权限。

角色和角色等级(Role Hierarchies)

角色本身仅仅只是一个名词,其本身并不能代表权限的大小。比如,我们可以定一个“Director”的角色,也可以定一个“Project Leader”的角色。对于现实中我们来说,看到这样两个角色,就清楚 DIR 的权限要比一个 PL 的权限级别高。但是对计算机来说,这两个角色仅仅是两个“词语”,是等同的。可以采用分等级角色,在角色上实现层次化来解决这些问题。也可以采用复合角色(其表示的就是一个角色组的概念),对角色实现一定的分组和复合,以便于权限指派。在一些 OA 产品中经常出现分等级角色。

限制(Constraints)

模型中的职责分离关系(Separation of Duty),用于控制冲突(Conflict)。静态职责分离(Static SD)指定角色的互斥关系,用于用户指派阶段。避免同一用户拥有互斥的角色。实现简单,角色互斥语义关系清楚,便于管理不够灵活,不能处理某些实际情况。动态职责分离(Dynamic SD)指定角色的互斥关系,用于角色激活阶段。允许同一用户拥有某些互斥的角色,但是不允许该用户同时激活互斥的角色。更灵活,直接与会话挂钩,适应实际管理需要,实现复杂,不易管理。

 

参考文献

《AN INTRODUCTION TO ROLE-BASED ACCESS CONTROL》 NIST《工作流授权控制模型》        胡长城

《基于角色的权限管理综述》 俞诗鹏

最后,感谢宏云博士对本文翻译提供的指导。

请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:http://www.blogjava.net/rosen

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

Spring的listener启动异常

October 23, 2006

碰到一个奇怪的问题,Spring在启动的时候得listener提示启动失败,打开log也没有任何信息,最后把log4j打开 :

<?xml version=”1.0″ encoding=”UTF-8″ ?>
<!DOCTYPE log4j:configuration SYSTEM ”log4j.dtd”>

<log4j:configuration xmlns:log4j=”http://jakarta.apache.org/log4j/”>

<appender name=”CONSOLE” class=”org.apache.log4j.ConsoleAppender”>
<layout class=”org.apache.log4j.PatternLayout”>
<param name=”ConversionPattern”
value
=”%p - %C{1}.%M(%L) | %m%n”/>
</layout>
</appender>

<logger name=”org.apache”>
<level value=”WARN”/>
</logger>

<logger name=”net.sf.hibernate”>
<level value=”WARN”/>
</logger>

<logger name=”org.springframework”>
<level value=”DEBUG”/>
</logger>

<!–
<logger name=”org.appfuse”>
<level value=”DEBUG”/>
</logger>
–>
<root>
<level value=”DEBUG”/>
<appender-ref ref=”CONSOLE”/>
</root>

</log4j:configuration>
提示说applicationContext.xml的编码有问题,最后改为UTF-8解决

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

[zt]hibernate二级缓存攻略

October 23, 2006

转自 javaEye
很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了。
我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化。

hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了。

二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache等,需要设置hibernate.cache.provider_class,我们这里用ehcache,在2.1中就是
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查询缓存,加上
hibernate.cache.use_query_cache=true

缓存可以简单的看成一个Map,通过key在缓存里面找value。

Class的缓存
对于一条记录,也就是一个PO来说,是根据ID来找的,缓存的key就是ID,value是POJO。无论list,load还是 iterate,只要读出一个对象,都会填充缓存。但是list不会使用缓存,而iterate会先取数据库select id出来,然后一个id一个id的load,如果在缓存里面有,就从缓存取,没有的话就去数据库load。假设是读写缓存,需要设置:
<cache usage=”read-write”/>
如果你使用的二级缓存实现是ehcache的话,需要配置ehcache.xml
<cache name=”com.xxx.pojo.Foo” maxElementsInMemory=”500″ eternal=”false” timeToLiveSeconds=”7200″ timeToIdleSeconds=”3600″ overflowToDisk=”true” />
其中eternal表示缓存是不是永远不超时,timeToLiveSeconds是缓存中每个元素(这里也就是一个POJO)的超时时间,如果eternal=”false”,超过指定的时间,这个元素就被移走了。timeToIdleSeconds是发呆时间,是可选的。当往缓存里面put 的元素超过500个时,如果overflowToDisk=”true”,就会把缓存中的部分数据保存在硬盘上的临时文件里面。
每个需要缓存的class都要这样配置。如果你没有配置,hibernate会在启动的时候警告你,然后使用defaultCache的配置,这样多个class会共享一个配置。
当某个ID通过hibernate修改时,hibernate会知道,于是移除缓存。
这样大家可能会想,同样的查询条件,第一次先list,第二次再iterate,就可以使用到缓存了。实际上这是很难的,因为你无法判断什么时候是第一次,而且每次查询的条件通常是不一样的,假如数据库里面有100条记录,id从1到100,第一次list的时候出了前50个id,第二次 iterate的时候却查询到30至70号id,那么30-50是从缓存里面取的,51到70是从数据库取的,共发送1+20条sql。所以我一直认为 iterate没有什么用,总是会有1+N的问题。
(题外话:有说法说大型查询用list会把整个结果集装入内存,很慢,而iterate只select id比较好,但是大型查询总是要分页查的,谁也不会真的把整个结果集装进来,假如一页20条的话,iterate共需要执行21条语句,list虽然选择若干字段,比iterate第一条select id语句慢一些,但只有一条语句,不装入整个结果集hibernate还会根据数据库方言做优化,比如使用mysql的limit,整体看来应该还是 list快。)
如果想要对list或者iterate查询的结果缓存,就要用到查询缓存了

查询缓存
首先需要配置hibernate.cache.use_query_cache=true
如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
<cache name=”net.sf.hibernate.cache.StandardQueryCache”
maxElementsInMemory=”50″ eternal=”false” timeToIdleSeconds=”3600″
timeToLiveSeconds=”7200″ overflowToDisk=”true”/>
<cache name=”net.sf.hibernate.cache.UpdateTimestampsCache”
maxElementsInMemory=”5000″ eternal=”true” overflowToDisk=”true”/>
然后
query.setCacheable(true);//激活查询缓存
query.setCacheRegion(“myCacheRegion”);//指定要使用的cacheRegion,可选
第二行指定要使用的cacheRegion是myCacheRegion,即你可以给每个查询缓存做一个单独的配置,使用setCacheRegion来做这个指定,需要在ehcache.xml里面配置它:
<cache name=”myCacheRegion” maxElementsInMemory=”10″ eternal=”false” timeToIdleSeconds=”3600″ timeToLiveSeconds=”7200″ overflowToDisk=”true” />
如果省略第二行,不设置cacheRegion的话,那么会使用上面提到的标准查询缓存的配置,也就是net.sf.hibernate.cache.StandardQueryCache

对于查询缓存来说,缓存的key是根据hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
参数是”tiger%”,那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了):
select * from cat c where c.name like ? , parameter:tiger%
这样,保证了同样的查询、同样的参数等条件下具有一样的key。
现在说说缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。
可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。
这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,这种情况就请自己注意了。

另外,如果hql查询包含select字句,那么查询缓存里面的value就是整个结果集了。

当hibernate更新数据库的时候,它怎么知道更新哪些查询缓存呢?
hibernate在一个地方维护每个表的最后更新时间,其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。
当通过hibernate更新的时候,hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表,当hibernate查询一个缓存是否存在的时候,如果缓存存在,它还要取出缓存的生成时间和这个缓存所查询的表,然后去查找这些表的最后更新时间,如果有一个表在生成时间后更新过了,那么这个缓存是无效的。
可以看出,只要更新过一个表,那么凡是涉及到这个表的查询缓存就失效了,因此查询缓存的命中率可能会比较低。

Collection缓存
需要在hbm的collection里面设置
<cache usage=”read-write”/>
假如class是Cat,collection叫children,那么ehcache里面配置
<cache name=”com.xxx.pojo.Cat.children”
maxElementsInMemory=”20″ eternal=”false” timeToIdleSeconds=”3600″ timeToLiveSeconds=”7200″
overflowToDisk=”true” />
Collection的缓存和前面查询缓存的list一样,也是只保持一串id,但它不会因为这个表更新过就失效,一个collection缓存仅在这个collection里面的元素有增删时才失效。
这样有一个问题,如果你的collection是根据某个字段排序的,当其中一个元素更新了该字段时,导致顺序改变时,collection缓存里面的顺序没有做更新。

缓存策略
只读缓存(read-only):没有什么好说的
读/写缓存(read-write):程序可能要的更新数据
不严格的读/写缓存(nonstrict-read-write):需要更新数据,但是两个事务更新同一条记录的可能性很小,性能比读写缓存好
事务缓存(transactional):缓存支持事务,发生异常的时候,缓存也能够回滚,只支持jta环境,这个我没有怎么研究过

读写缓存和不严格读写缓存在实现上的区别在于,读写缓存更新缓存的时候会把缓存里面的数据换成一个锁,其他事务如果去取相应的缓存数据,发现被锁住了,然后就直接取数据库查询。
在hibernate2.1的ehcache实现中,如果锁住部分缓存的事务发生了异常,那么缓存会一直被锁住,直到60秒后超时。
不严格读写缓存不锁定缓存中的数据。

使用二级缓存的前置条件
你的hibernate程序对数据库有独占的写访问权,其他的进程更新了数据库,hibernate是不可能知道的。你操作数据库必需直接通过 hibernate,如果你调用存储过程,或者自己使用jdbc更新数据库,hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的,但是据说3.1已经解决了这个问题。
这个限制相当的棘手,有时候hibernate做批量更新、删除很慢,但是你却不能自己写jdbc来优化,很郁闷吧。
SessionFactory也提供了移除缓存的方法,你一定要自己写一些JDBC的话,可以调用这些方法移除缓存,这些方法是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不过我不建议这样做,因为这样很难维护。比如你现在用JDBC批量更新了某个表,有3个查询缓存会用到这个表,用evictQueries (String cacheRegion)移除了3个查询缓存,然后用evict(Class persistentClass)移除了class缓存,看上去好像完整了。不过哪天你添加了一个相关查询缓存,可能会忘记更新这里的移除代码。如果你的 jdbc代码到处都是,在你添加一个查询缓存的时候,还知道其他什么地方也要做相应的改动吗?

—————————————————-

总结:
不要想当然的以为缓存一定能提高性能,仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用,可能会有1+N的问题。不当的使用还可能导致读出脏数据。
如果受不了hibernate的诸多限制,那么还是自己在应用程序的层面上做缓存吧。
在越高的层面上做缓存,效果就会越好。就好像尽管磁盘有缓存,数据库还是要实现自己的缓存,尽管数据库有缓存,咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么,只能做的比较通用,而高层可以有针对性的实现缓存,所以在更高的级别上做缓存,效果也要好些吧。

终于写完了,好累……

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

在jsp中获得 Spring的上下文

October 23, 2006

在Struts或别的框架中集成Spring的时候,Spring向我们提供了获得context的方法 getApplicationContext,那在jsp中如何获得呢?

ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext
(
this.getServletConfig().getServletContext());
建议在项目中开发的时候提供一个singleton对外公布统一的applicationContext,毕竟不是每个人都一定能获得web环境或servlet.

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

用js实现打印和导出为Excel

October 21, 2006

打印可以用控件实现,保存为excel也可以用POI实现,不过如果仅仅是对当前页面的指定区域作打印或者Excel导出可以用js实现,还是挺简单的.

保存为Excel:

function saveAsExcel(HeadName, DivName) {
var s = <center> + HeadName + </center> + \r\n;
+= DivName.innerHTML;
var xlsWindow = window.open(“”_blankwidth=1,height=1,scrollbars=no,toolbar=no);
xlsWindow.document.write(s);
xlsWindow.document.close();
xlsWindow.document.execCommand(‘Saveas’, 
true, ’%homeDrive%\\Data.xls’)
xlsWindow.close();
}

打印当前页面:

function PrintDataSoure(HeadName1,HeadName2,HeadName3,DivName,TailName1) {
var oldBody=document.body.innerHTML;
var Div1=DivName.innerHTML;
var css = ’<style type=text/css media=all>‘ +
‘p { line
-height: 120%}’ +
‘.fhead {   font
-size: 9pt; text-decoration: none; color: 104A7B}’ +
‘.ftitle { line
-height: 120%; font-size: 18px; color: #000000}’ +
‘td { font
-size: 10px; color: #000000}’ +
</style>‘ ;

var body =<table width=640 border=0 cellspacing=0 cellpadding=5>‘ +
‘ 
<tr> ’ +
‘ 
<td class=fbody>‘ +
‘ 
<b><div align=center>+<font size=+1 class=fhead>+ HeadName1 + ’</div>+</font></b>+
‘ 
<b><div align=center>+<font size=+1 class=fhead>+ HeadName2 + ’     ’ + HeadName3 +</div></font></b>+
‘ 
</td>‘ +
‘ 
</tr>‘ +
‘ 
<tr> ’ +
‘ 
<td class=fbody>‘ +
‘ 
<div align=center class=ftitle>‘ + Div1 + ’</div>+
‘ 
<b><div align=right>+<font size=+1 class=fhead>+ ’      </div>+</font></b>+
‘ 
<b><div align=right>+<font size=+1 class=fhead>+ TailName1 + ’</div>+</font></b>+
‘ 
</td>‘ +
‘ 
</tr>‘ +
</table>‘;
document.body.innerHTML 
= ’<center>‘ + css + body +<OBJECT classid=CLSID:8856F961-340A-11D0-A96B-00C04FD705A2 height=0 id=wb name=wb width=0></OBJECT>+</center>‘;
wb.execwb(
6,6);
document.body.innerHTML
=oldBody;
}

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

[zt]JdbcTemplate使用指南

October 16, 2006

key words:Spring,jdbcTemplate

注:因为Spring是以后的一个趋势,Hibernate的集成已经很好了,对于单独的jdbc的操作用DBUtils感觉已经没有什么必要,不如全部转到Spring的jdbc支持,从成本来考虑似乎更合适。

本文转自 这里


前言:

本文指在介绍 Spring 框架中的 JdbcTemplate 类的使用方法,涉及基本的 Spring 反转控制的使用方法和 JDBC 的基本概念。目标是使读者能够对 JdbcTemplate 快速地掌握和使用。

 

       准备:

1. Spring 的基本概念

       Spring 框架核心的思想就是建立一个 Java 对象的大工厂,用户只要给工厂一个指令,工厂就能将用户需要的对象根据配置文件组装好返还给用户。用户需要做的许多工作则可以写成简单的配置文件。

       2. 丑陋的 JDBC 代码

Connection con= null;

PreparedStatement pStmt=null;

ResultSet rs = null;

try{          

            con = ods.getConnection();

            String sql = “select * from admin”;

            pStmt=con.prepareStatement(sql);           

            rs=pStmt.executeQuery();

            while(rs.next())

            {            }

}

catch(Exception ex) {

try{

         con.rollback();

    }catch(SQLException sqlex){

          sqlex.printStackTrace(System.out);

     }

     ex.printStackTrace();

}finally{

   try{

            rs.close();

            pStmt.close();

            con.close();

   }catch(Exception e){e.printStackTrace();}

}

 

 

       以上是常见的 JDBC 代码,简单的 select 语句也需要冗长的出错处理,并且每个函数都不断地重复同样的代码。

 

       3. JdbcTemplate 的作用

       JdbcTemplate 正是为了减少上述繁琐的代码而设计出来的。它是对 JDBC 的一种封装,抽象我们常用的一些方法。 Simple and Stupid 就是它的目标。下面是完成了刚才 JDBC 代码同样功能的 JdbcTemplate 的代码:

String sql = “select * from admin”;

jdbcTemplate.query(sql,new RowCallbackHandler() {

         public void processRow(ResultSet rs) throws SQLException {   

                }

            } );

 

 

       环境搭建:

1. 数据库的配置

       本文使用 Oracle 数据库,新建表 admin

create table admin (
       IDnumber(
10 ) primarykey,
       NAMEvarchar2(
64 ),
       PASSWORDvarchar2(
64 )
)

 

 

       2. Spring 配置

       JdbcTemplate 的使用需要有 DataSource 的支持,所以在配置文件中,我们首先要配置一个 OracleDataSource ,然后在将这个 DataSource 配置到 JdbcTemplate 里。接着将 JdbcTemplate 配置进 DAO 层,最后将 DAO 配置进 Model 层。简要的关系如下:

 

      

模型层 : User

数据访问层:UserDAO

JdbcTemplate

OracleDataSource

<!–[if !vml]–> <!–[endif]–>

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE beans PUBLIC “-//SPRING//DTD BEAN//EN”

    “http://www.springframework.org/dtd/spring-beans.dtd”>

<beans>

    <bean id=”dataSource” class=”oracle.jdbc.pool.OracleDataSource”>

        <property name=”URL”>

            <value>jdbc:oracle:thin:root/123@localhost:1521/XE</value>

        </property>

    </bean>

   

    <bean id=”jdbcTemplate”

class=”org.springframework.jdbc.core.JdbcTemplate”>

        <property name=”dataSource”><ref bean=”dataSource”/></property>

    </bean>

 

    <bean id=”userDAO” class=”DAO.Imp.UserDAOImp”>

        <property name=”jdbcTemplate”>

<ref bean=”jdbcTemplate” />

</property>

    </bean>

   

    <bean id=”user” class=”Model.User”>

        <property name=”dao”><ref bean=”userDAO”/></property>

    </bean>

</beans>

 

       3. 环境配置, 如图:

       <!–[if !vml]–> <!–[endif]–>

 

使用方法:

<!–[if !supportLists]–> 1.       <!–[endif]–> 查找

多行查询:

class UserRowMapper implements RowMapper {

        public Object mapRow(ResultSet rs,int index) throws SQLException

        {

            User u = new User();

            u.setId(rs.getString(“ID”));

            u.setName(rs.getString(“Name”));

            u.setPassword(rs.getString(“Password”));

            return u;

        }

    }

public List select(String where)

    {

        List list;       

        String sql = “select * from admin “+where;       

        list = jdbcTemplate.query(sql,new RowMapperResultReader(new UserRowMapper()));

        return list;

    }

 

 

 

List 最终返回的是满足条件的 User 队列。

 

单行查询:

public User selectById(String id){

    String sql = “select * from admin where id=?”;

    final User u = new User();

    final Object[] params = new Object[] {id};

    jdbcTemplate.query(sql, params, new RowCallbackHandler(){

                        public void processRow(ResultSet rs) throws SQLException {

                                 u.setId(rs.getString(“ID”));

                                 u.setName(rs.getString(“NAME”));

                                 u.setPassword(rs.getString(“PASSWORD”));

                        }                     

    });         

    return u;

}

 

 

 

<!–[if !supportLists]–> 2.       <!–[endif]–> 插入

public void insert(User u)

{

     String sql = “insert into admin (ID,NAME,PASSWORD) values (admin_id_seq.nextval,?,?)”;

     Object[] params = new Object[] {

                                                u.getName(),

                                                u.getPassword() };

     jdbcTemplate.update(sql,params);

 }

 

admin_id_seq.nextval Oracle 设置好的序列,问号“ ? ”被 params 里的数据依次替代,最终执行 sql

 

<!–[if !supportLists]–> 3.       <!–[endif]–> 修改

非常简单:

public void update(String how)

{

        jdbcTemplate.update(how);

    }

 


源代码:

User.class:

package Model;

 

import java.util.List;

import DAO.UserDAO;

/**

 *  Model

 *

 *

 * @author 李嘉陵

 * @since 2006-4-30 12:10:30

 * @version 0.10a

 **/

 

public class User {

    private String name;

    private String id;

    private String password;

    private UserDAO dao;

   

    public User()

    {

      

    }

   

    public User(String name, String password)

    {

       this.name = name;

       this.password = password;

    }

   

    public void setDao(UserDAO dao)

    {

        this.dao = dao;

    }

    public String getId() {

        return id;

    }

   

    public void setId(String id) {

        this.id = id;

    }

   

    public String getName() {

        return name;

    }

   

    public void setName(String name) {

        this.name = name;

    }

   

    public String getPassword() {

        return password;

    }

   

    public void setPassword(String password) {

        this.password = password;

    }

   

    public void getInfo(String id)

    {

        List list = dao.select(“where id=”+id);

        User u = (User) list.get(0);

       

        this.id=id;

        this.name = u.getName();

        this.password = u.getPassword();

       

    }

   

    public void insert()

    {

        dao.insert(this);

    }

   

    public void update(String how)

    {

        dao.update(how);

    }

   

    public void update()

    {

        dao.update(“update admin set name=’”+name+”‘, password=’”+password+”‘ where id=”+id);

    }

   

    public List selectWithTemp(String where)

    {

        return dao.select(where);

    }

   

    public void selectWithTemp()

    {

        dao.selectWithTemp();

    }

   

    public User selectById(String id)

    {

       return dao.selectById(id);

    }

    

    public void insertUsers(List users)

    {

       dao.insertUsers(users);

    }

}

 

 

UserDAO.class :

package DAO;

 

import java.util.List;

 

import Model.User;

 

/**

 * DAO 层接口

 *

 *

 * @author 李嘉陵

 * @since 2006-4-30 8:40:56

 * @version 0.10a

 **/

 

public interface UserDAO {

    public void select();

    public void test();

    public void selectWithTemp();

    public List select(String where);

    public void update(String how);

    public void insert(User u);

    public User selectById(String id);

    public int[] insertUsers(final List users);

   

}

 

UserDAOImp.class:

package DAO.Imp;

 

 

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.List;

 

import org.springframework.jdbc.core.BatchPreparedStatementSetter;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.core.RowCallbackHandler;

import org.springframework.jdbc.core.RowMapper;

import org.springframework.jdbc.core.RowMapperResultReader;

 

import DAO.UserDAO;

import Model.User;

 

/**

 *  DAO 层的实现

 *

 *

 * @author 李嘉陵

 * @since 2006-4-30 8:41:26

 * @version 0.10a

 **/

 

public class UserDAOImp implements UserDAO{

   

    private JdbcTemplate jdbcTemplate;

   

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate)

    {

        this.jdbcTemplate = jdbcTemplate;

    }

   

    class UserRowMapper implements RowMapper

    {

        public Object mapRow(ResultSet rs,int index) throws SQLException

        {

            User u = new User();

            u.setId(rs.getString(“ID”));

            u.setName(rs.getString(“Name”));

            u.setPassword(rs.getString(“Password”));

 

            return u;

        }

    }

   

    public void selectWithTemp()

    {

        String sql = “select * from admin”;

              

        jdbcTemplate.query(sql,new RowCallbackHandler() {

                public void processRow(ResultSet rs) throws SQLException {

                System.out.println(“ID: “+rs.getString(“ID”)+”   Name: “+rs.getString(“name”));

                }

            } );

 

    }

   

    public List select(String where)

    {

        List list;

        String sql = “select * from admin “+where;

        list = jdbcTemplate.query(sql,new RowMapperResultReader(new UserRowMapper()));

        return list;

    }

   

    public User selectById(String id)

    {

       String sql = “select * from admin where id=?”;

       final User u = new User();

       final Object[] params = new Object[] {id};

      

       jdbcTemplate.query(sql,params, new RowCallbackHandler(){

                         public void processRow(ResultSet rs) throws SQLException {

                                   u.setId(rs.getString(“ID”));

                                   u.setName(rs.getString(“NAME”));

                                   u.setPassword(rs.getString(“PASSWORD”));

                         }

       });

      

       return u;

    }

   

    public void update(String how)

    {

        String sql = how;

        jdbcTemplate.update(sql);

    }

   

    public void insert(User u)

    {

        String sql = “insert into admin (ID,NAME,PASSWORD) values (admin_id_seq.nextval,?,?)”;

        Object[] params = new Object[] {

                                                   u.getName(),

                                                   u.getPassword()};

        jdbcTemplate.update(sql,params);

    }

  

}

 

 

UserAction.class:

// 测试类

public class UserAction {

    public static void main(String[] args)

    {

        Resource resource=new ClassPathResource(“beans.xml”);

        BeanFactory factory = new XmlBeanFactory(resource);       

        User user = (User) factory.getBean(“user”);   

       

        user.selectWithTemp();

    }

}

 

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

谈谈 Facade与Proxy的联系与区别

October 10, 2006

key words:  门面与代理,设计模式

这两组内容没有联系,只是今天在路上的时候突然一下子想到了,就放在一起说说吧

Facade与proxy大多数人应该很熟悉,也接触了不少,但是放在一起比较问他们有什么具体区别的时候好像一下子也说得很含糊。
Facade 意为门面,proxy意为代理,中文的解释也很通俗,但是有时候感觉Facade也有代理的味道,有人认为二者都属于proxy这个大概念,但是问题是他们总得有不同的用途吧,否则发明这两个词岂不是浪费?

google了一些说法,下面这个说法比较到位:

Facade: 用于隐藏调用的复杂性
proxy: 放在服务期端保护被访问的对象

我个人的理解是:
对于客户端掉用来说,Facade是一定可见的,proxy是不可见的 .

比如,一个报刊亭卖报纸,可能有人民日报,新华日报等多家送报纸给他们的人,但是我们直接到他的店面就可以取到我们想要的报纸,而不是买人民日报的时候到人民日报社去取,以此类推.
而proxy对我们是隐藏的,比如Jive论坛里的权限验证,我们发帖子的时候客户端是透明访问的,压根实际在服务端后台作了isAdmin和isGuest的判断

Facade仅仅是提供了你一个方便的 “门面”,你可以把它理解为超市,你只要到他那里就可以取到你想要的东西,但是他所调用的后台仍然可以根据策略开放给客户端,比如我们完全可以不去报刊亭买报纸,我们自己去人民日报社去取,但是这种做法显然在如今的现代化潮流下是没什么提倡的,现在更提倡分工.

对于proxy我们仍有一个误解,比如问你”中介公司是proxy吗?” 请思考一下

很多人认为中介公司是代理,因为我们生活中有时候就直接称中介公司为代理商或代理公司等,但是此代理非彼代理也!  为什么? 参照我们上面所说的proxy的特征,proxy是隐藏的,难道我们联系代理公司的地址时他会说 : “打一枪换一个地方” 么?  显然不是,恰恰相反,中介公司事实上反而是我们java里的Facade模式,因为它是可见的.

好,关于facade 与proxy就说到这里,应该很清楚了.

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)

关于Facade的应用

October 9, 2006

Facade用的非常的广了,以前刚接触的时候有个误解,总觉得Facade是简单的,而它后面的支撑服务是复杂的,对于客户来说却是简单的,现在来看,不完全对,或者说只是说对了一半,因为有时候恰恰是Facade是复杂的.

我们举一个例子,比如发送短信,我们一般就定义一个MessageService的服务类,里面只提供一个方法就行了,sendToUser(String phone,String content)
但是到了客户端的时候有了自己的 “方言”,比如它不是关心一个抽象的用户,它只知道向教师发送短信,或者向学生发送短信,或者向家长发送短信。

示例如下:

facade
由图中可以看到,Facade的内容非常丰富,而支撑它的服务类却很简单,在开发过程中我们一般先实现通用的ServiceA,然后根据进一步的需求做一个面向具体复杂的Facade.

在Spring提供的sample里发现一个小技巧,就是Facade和ServiceA都是接口,然后提供一个实现二者的支撑类:

public class PetStoreAnnotationImpl implements PetStoreFacade, OrderService {

private AccountDao accountDao;

private CategoryDao categoryDao;

private ProductDao productDao;

private ItemDao itemDao;

private OrderDao orderDao;

//————————————————————————-
// Setter methods for dependency injection
//————————————————————————-

public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}

public void setCategoryDao(CategoryDao categoryDao) {
this.categoryDao = categoryDao;
}

public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}

public void setItemDao(ItemDao itemDao) {
this.itemDao = itemDao;
}

public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}

//————————————————————————-
// Operation methods, implementing the PetStoreFacade interface
//————————————————————————-

public Account getAccount(String username) {
return this.accountDao.getAccount(username);
}

public Account getAccount(String username, String password) {
return this.accountDao.getAccount(username, password);
}

public void insertAccount(Account account) {
this.accountDao.insertAccount(account);
}

public void updateAccount(Account account) {
this.accountDao.updateAccount(account);
}

public List getUsernameList() {
return this.accountDao.getUsernameList();
}

public List getCategoryList() {
return this.categoryDao.getCategoryList();
}

public Category getCategory(String categoryId) {
return this.categoryDao.getCategory(categoryId);
}

public List getProductListByCategory(String categoryId) {
return this.productDao.getProductListByCategory(categoryId);
}

public List searchProductList(String keywords) {
return this.productDao.searchProductList(keywords);
}

public Product getProduct(String productId) {
return this.productDao.getProduct(productId);
}

public List getItemListByProduct(String productId) {
return this.itemDao.getItemListByProduct(productId);
}

public Item getItem(String itemId) {
return this.itemDao.getItem(itemId);
}

public boolean isItemInStock(String itemId) {
return this.itemDao.isItemInStock(itemId);
}

public void insertOrder(Order order) {
this.orderDao.insertOrder(order);
this.itemDao.updateQuantity(order);
}

public Order getOrder(int orderId) {
return this.orderDao.getOrder(orderId);
}

public List getOrdersByUsername(String username) {
return this.orderDao.getOrdersByUsername(username);
}

}

看起来似乎不错,不过仔细想想个人认为还是不是太好,总的感觉就是层次不清晰,因为很多时候Facade和Service之间是被服务与服务的关系,所以理当分开。 同时,这个类有点倾向于”万能类”了,能分还是分开好.这和我们以前提到的dao又背离过来了(以前我们提倡一个业务一个dao,现在觉得只用一个通用的dao更合适),这个并不矛盾,具体问题具体看待.

欢迎各位拍砖.

VN:F [1.6.3_896]
Rating: 0.0/10 (0 votes cast)
VN:F [1.6.3_896]
Rating: 0 (from 0 votes)