Ralasafe Cookbook
概述
功能概述
Ralasafe访问控制中间件(数据级权限管理),是MIT协议开源免费软件产品。它使用图形化方式处理各种数据级权限管理问题,包括数据库行列级、字段内容级权限问题;它还能处理登录控制、功能级权限控制和URL权限认证。
使用获益
使用Ralasafe,可以做到:
-
权限与业务解耦合——实践更清晰的系统架构
-
权限管理完全图形化管理,不需要写一行代码或XML文件(基本配置信息除外,如数据源和用户元数据)——实践高效开发
-
所有访问控制被集中、规范化管理——实践访问模式的统一,减少安全漏洞
Ralasafe完全遵循JAVA EE规范,跨平台、web服务器和主流数据库。
架构与部署
架构
Ralasafe访问控制中间件包括2大部分:
-
权限引擎
-
Web 管理控制端
权限引擎是jar包,1.1RC1版本大小846KB,非常精简。引用了castor、beanshell、jakarta-commons、gson等第三方包,共6.69M。权限引擎负责对权限策略进行解析,并返回解析结果。
Web 管理控制端,使用jQuery+jsp技术编写,展开大小1.79M。Web 控制端则负责图形化方式定制权限策略,在线图形化方式测试权限策略。
Ralasafe没有使用任何第三方框架,虽然给Ralasafe开发带来一些难度。但给集成Ralasafe开发者带来了更多福音:
-
Ralasafe中间件无框架要求,可以和任何框架集成
-
Ralasafe中间件无框架,和你将选用(或者已选用)的框架无冲突
部署
权限引擎是必须与应用集成部署的。
Web 管理控制端可选部署。你可以将Web 管理控制端与应用一起部署;你也可以只在开发 阶段使用,在生产部署时,不部署Web 管理控制端。
权限与业务解耦合
解耦合理论分析
权限与业务解耦合,非常有必要,也非常难。因为权限、尤其是数据级权限,与业务紧密耦合。有的时候甚至很难区分哪个是权限、哪个是业务;另外,需求变化也经常影响到权限模型、业务模型的变动。所有这些造成“数不清理还乱”的感觉。
只有实现如下目标,才能做到权限与业务解耦合,并适应不同需求和需求变化:
-
权限模型和业务模型,可以随时独立变动(增加、减少、修改等)
-
权限逻辑的变化,可以独立于业务进行修改也就是说,我们需要一个缓冲带、中间件!
Ralasafe做更多
Ralasafe就是这样的中间件,它能够适配业务模型、数据模型和权限模型,并将权限逻辑独立于业务进行集中管理。Ralasafe不仅实现了上述目标,而且做了更多:
-
集中管理权限
-
图形化管理权限
-
图形化测试权限
-
对权限逻辑变动,无需重启应用,即时生效
Ralasafe适配业务模型:查询权限可以将数据以你指定的业务模型(Javabean)格式返回;权限运算可以“钻取”业务数据的属性,进行权限规则计算; Ralasafe适配数据模型:可以在线读取数据库表结构,在线定制数据查询,并指定查询返回结果以哪个业务模型(Javabean)格式返回; Ralasafe适配权限模式:随时增加、修改权限模式,随时增加、修改权限策略。
权限API
Ralasafe API在你需要权限判断的地方,调用一下;就像LOG4J API那样,你在需要写日志的地方,调用一下。
功能级权限判断API一个;数据级权限判断API两个,分别对应从系统获取数据或者向系统提交数据两个方向——又称为查询权限、决策权限。
package org.ralasafe; import org.ralasafe.entitle.Decision; import org.ralasafe.entitle.QueryResult; import org.ralasafe.user.User; public class Ralasafe { public static boolean hasPrivilege(int privilegeId, User user) { .... } public static QueryResult query(int privilegeId, User user, Map context) { .... } public static Decision permit(int privilegeId, User user, Object businessData, Map context) { .... } }
API非常简单,除了权限id(int privilegeId)外,不包含其他任何权限信息。也只有这样才能做到权限与业务解耦合。 所有权限逻辑全部在权限策略里面。当应用系统请求Ralasafe API的时候,Ralasafe权限引擎将解析相关权限策略,并返回解析结果。(如前面所说,权限策略由Ralasafe Web控制端图形化定制管理。)
权限ID
权限ID用来标识当前请求的是什么权限。查询员工权限呢,还是订单修改权限。权限ID可以通过Ralasafe Web 控制端导出,然后保存到你的业务常量类里面,如com.your.company.Constants。
调用API时,直接使用常量引用,不需要使用数字了。如:
Ralasafe.hasPrivilege( Constants.QUERY_EMPLOYEE, user )
用户信息
权限判断还要告知当前用户是谁。该用户应该是成功通过身份验证的用户。你可以使用Ralasafe自带的LoginFilter来构建User,或者自行构建User对象。
自行构建User 对象非常简单:
import org.ralasafe.user.User; User user=new User(); user.setId( userId ); user.set( "field1", value1 ); user.set( "field2", value2 );
new一个对象,然后使用set对User的各个属性进行设置。(Ralasafe对用户属性描述,通过用户元数据进行描述。描述用户表在哪里,用户表有哪些字段,分别代表什么业务意义。)
上下文
Map context指上下文,有的权限策略需要从上下文获取值进行权限计算,此时就需要提供上下文;有的权限策略不需要上下文,此时可以不必提供上下文,直接用null做参数。
我们考察这个策略:本人当前借款记录。该策略应该对应数据查询:
SELECT * FROM LoanMoney WHERE userId=? AND loanDate=?
-
其中第一个参数是用户id,Ralasafe引擎可以直接从User中“钻取”获得;
-
第二个参数是借款日期,表示当天。此时我们可以将当天日期放入上下文,Ralasafe自动从上下文“钻取”。如果我们约定该上下文键值(key)是today,那么调用Ralasafe的API是:
Map context=new HashMap(); context.put( "today", new java.util.Date() ); Ralasafe.query( privilegeId, user, context );
查询返回结果
Ralasafe查询权限返回QueryResult,包含这些信息:
-
Collection<com.your.company.JavabeanAbc>,以你的业务模型格式返回的查询结果;如果没用查询权限,返回空集合(非NULL)
-
int totalCount,为界面分页显示提供的,如果完整执行该查询可以返回总记录条数
-
Collection<String> fields,当前用户允许查询业务模型哪些字段。比如订单有10个字段,当前用户能查看6个字段,这就表示那6个字段(注:如果当前用户只能查看6个字段,返回的Javabean也只设置这6个字段值,其他4个字段不予查询和设置值)
-
Collection<String> readOnlyFields,当前用户对哪些字段是只读的,不能进行修改
决策返回结果
Ralasafe决策权限返回结果Decision,包含这些信息:
-
boolean permit,是否允许当前用户对该数据(Object businsessData)进行操作
-
String denyReason,如果拒绝当前用户对该数据(Object businessData)进行操作,拒绝理由是什么。
其他API
org.ralasafe.Ralasafe围绕这2大API还提供了其他API,如自定义条件查询 (输入CustomizedWhere条件),分页查询,只查询记录条数queryCount等
org.ralasafe.WebRalasafe是针对Web应用程序对Ralasafe进行封装。用户数据直接从 request.getSession()中获取,将拒绝理由设置到request.setAttribute()等,方便web程序的交互方式。可以参 考源码或者JavaDoc,此处不做详述。
权限模型概述
简介
Ralasafe权限控制包括2大部分:功能级权限控制和数据级权限控制。功能级权限控制使用通用的RBAC模型,即给用户赋予角色,给角色赋予权限。
Ralasafe通过对RBAC模型做简单扩展实现数据级权限,即给每个权限赋予一条或者多条权限策略。权限策略主要有:用户分类和资源两大要素组成。
当Ralasafe API被请求时,权限引擎会首先验证当前用户是否有功能权限;如果有功能权限,然后继续进行数据级权限验证。
核心概念
Ralasafe核心概念有:
-
用户分类
-
资源,主要是
-
业务数据分类
-
数据查询
-
这些概念并不是凭空捏造,都是来源于现实的权限需求,只是平常我们都会忽略了它们。让我们细细想想:
-
权限问题是否都是在探讨这样的问题:当什么情况下,怎么怎么样?当另一种情况下,又怎么怎么样?当另一种情况下,又怎么怎么样?……
-
这样的问题,总是可以用这种方式来解答:什么样的用户能看到哪些数据,操作哪些数据;什么样的用户又能看到哪些数据,操作哪些数据;…….
什么样的用户,就是“用户分类”! 能看到哪些数据,就是“数据查询”!! 操作哪些数据,就是“业务数据分类”!!!
用户分类
绝大部分人听到用户分类,都会想到角色。诚然,角色和用户分类都是对用户进行划分。但角色是一种静态划分;用户分类属于动态划分,是一种动态用户群组。
静态划分意思是:我们首先将某角色赋予给用户后,用户才隶属于该角色;
动态划分意思是:我们使用规则对用户进行描述,如果用户满足该规则描述,则属于该“用户分类”,无需事先将用户划到该用户群组。这种隶属关系是基于规则运算。
考察如下例子:
-
用户分类名称:总公司用户; 规则是:用户的机构ID=总公司机构ID
-
用户分类名称:分公司用户; 规则是:用户的机构 level=分公司机构level
-
用户分类名称:北京分公司用户;规则是:用户的机构ID=北京分公司机构ID
以上分类基于用户的属性进行规则判定。下面,再考察其他类型的分类:
用户分类名称:临时调查组用户;规则是:用户的id号在数据表临时调查组(SPEC_USER)有记录。
Ralasafe的用户分类是开放的,你可以通过Ralasafe Web控制端进行随意定制。还可以按照树形结构,将用户分类有规律的管理起来。
另外,通过Ralasafe创建角色的时候,Ralasafe会自动创建一个对应的用户分类——隶属该角色的用户分类。规则是:当前用户的角色集合中含有该角色,即如果当前创建的角色是管 理员角色,那么规则就是:当前用户的角色集合中含有管理员角色。
对于初学者,角色和用户分类概念不仅理解起来有点难度,使用起来也有一定难度。不知道什么情况下使用角色,什么情况下使用用户分类。
推荐准则:角色放在功能级控制层面;用户分类属于数据级控制层面。
总是思考这样的问题: “什么样的用户能看到哪些数据,操作哪些数据;什么样的用户又能看到哪些数据,操作哪些数据”。 就是考虑这个“样”,这个“范”,这个“style”。![]()
业务数据分类
业务数据分类和用户分类概念一致。用户分类是对用户的动态分类,基于规则描述,动态匹配计算;业务数据分类是对业务数据的动态分类,基于规则描述,动态匹配计算。
Ralasafe不限定该业务数据分类的类继承关系,也无需实现Ralasafe的某个接口类。只要是个JavaBean 就可以了。Ralasafe需要使用Java 反射技术对业务数据相关属性进行读取。比如 name属性,会反射调用getName()方法。
考察如下例子:
-
业务数据分类名称:大于5000的取款;规则是:金额字段值大于5000
继续考察如下复杂一些的例子:
-
业务数据分类名称:登录用户所在公司的订单;规则是:订单的机构id=登录用户的机构id
-
业务数据分类名称:加上本笔取款全体取款额大于20000;规则是:本笔取款额+数据查询(当天本人取款额之和)>20000
数据查询
数据查询和我们平常写的SQL查询概念一致,可以像Java PreparedStatement一样动态注入参数。
权限模型
有了上述概念后,我们详述一下Ralasafe的数据级权限管理模型。
查询权限模型
查询权限举例:人力专员按照机构层级查询员工。人力专员由RBAC角色控制,数据级权限策略如下:
| 策略序号 | 用户分类 | 数据查询 |
|---|---|---|
| 1 | 总公司用户 | 查询所有员工 |
| 2 | 分公司用户 | 查询所在分公司及下属营业部员工 |
| 3 | 营业部用户 | 查询所在营业部员工 |
决策权限模型
决策权限举例:每笔借款不超过5000元;每天借款总额又不能超过20000元。
| 策略序号 | 决策 | 用户分类 | 业务数据分类 | 拒绝理由 |
|---|---|---|---|---|
| 1 | 拒绝 | 所有人 | 大于5000的借款 | 单笔借款不能超过5000元 |
| 2 | 允许 | 所有人 | 加上本笔全天借款不超过20000的借款 | 全体借款总额不能超过20000元 |
权限数据来源
Ralasafe权限引擎会自动从固定值、用户、上下文、业务数据和数据库查询中“钻取”值,进行权限计算。权限规则可以是简单的比较计算(=,!=,& gt;,<,>=,⇐,),NULL判断计算(NULL, NOT NULL),IN集合比较(IN, NOT IN),也可以是上述规则组合形成规则组。规则组里面还可以嵌套规则组。
这些数据来源和计算规则,完全足够权限判断了。如果,你觉得不过瘾,Ralasafe还支持你书写原生态的Java代码,或者调用你的Java API。
在定制Ralasafe规则(用户分类、业务数据分类和数据查询)的时候,你可以使用这些权限数据。
固定值
就是确定值,不会变化的。比如定制用户分类——“总公司用户”时,规则是将用户的companyLevel属性与固定值1进行比较。
所以,我们定义了一个固定值变量headOfficeCompanyLevel,设置值等于1。
用户属性
如上面所说的“总公司用户”,需要读取用户属性companyLevel,我们定义了一个变量userCompanyLevel,设置值等于用户属性的 companyLevel。权限引擎计算时,会动态调用user.get( “companyLevel” ),然后将值付给userCompanyLevel。
定制数据查询——“登录用户所在公司”,其中where条件是company表的id字段等于用户的companyId属性。权限引擎计算时,会动态调用user.get( “companyLevel” ),设置到PreparedStatement查询参数里面。
业务数据属性
比如我们定制业务数据分类——“单笔借款大于5000元”。规则是用户数据的money属性与固定值5000做比较。权限引擎会自动调用传入对象的getMoney()方法获取值,与5000进行 比较。
LoanMoney money=new LoanMoney(); money.setLoanDate( new Date() ); User user=WebRalasafe.getCurrentUser(req); money.setUserId( ((Integer)user.get( User.idFieldName ) ).intValue() ); String strMoney=req.getParameter( "money" ); money.setMoney( Integer.parseInt( strMoney ) ); if( WebRalasafe.permit( req, Privilege.LOAN, money, context ) ) { loanManager.addLoadMoney( money ); }
如上代码,就会调用LoanMoney类的getMoney()方法。如果是其他类,也必须要有getMoney方法。
如果你指定业务数据属性是abc,那么该类必须要有getAbc()方法。总之,遵循通用的Javabean规范。
上下文值
比如我们定制数据查询——“本人当天借款记录”,其中有个where条件是LoanMoney表的loanDate属性等于“键值为today的上下文值”。
Map context=new HashMap(); context.put( "today", new java.util.Date() ); Ralasafe.query( privilegeId, user, context );
如上代码,我们将today放入上下文。权限引擎执行查询时,自动从上下文“钻取”参数,设置到PreparedStatement查询参数里面。
数据查询
Ralasafe的规则里面还能调用数据查询,将查询结果进行规则计算。比如,创建角色完毕后,自动生成的对应用户分类规则是:角色id是否在数据查询(当前用户所有角色集合)里面。 伪代码是:
roleId IN (Collection: query( SELECT roleId FROM ralasafe_ralasfe_role WHERE userId=${user.id} ) )
再比如业务数据分类——“加上本笔借款全体借款额不超过20000”。规则中也用到询——“查询本人当前总借款额”。该业务数据分类的规则是:
20000 - query( SELECT sum(money) WHERE userid=${user.id} AND loanDate=${context.today} ) >=${businessData}.money。
查询权限模型
查询权限举例:人力专员按照机构层级查询员工。人力专员由RBAC角色控制,数据级权限策略如下:
| 策略序号 | 用户分类 | 数据查询 |
|---|---|---|
| 1 | 总公司用户 | 查询所有员工 |
| 2 | 分公司用户 | 查询所在分公司及下属营业部员工 |
| 3 | 营业部用户 | 查询所在营业部员工 |





浙公网安备 33010602011771号