第六章
细粒度数据操作权限
很多系统对于黑客不堪一击。请看这样的示例:
1, 前台展现客户能查看的客户数据,而且用户能删除的客户数据,就是前台展现出来的数据;
2, 当用户选择某个用户,点击删除按钮,后台执行删除操作。
比如,请求后台删除的 url 是: http://www.test.com/crm/customer.do?id=3
假设, id=13 的客户在前台不显示(因为当前用户没有对该客户数据有删除权限),但用户输入 http://www.test.com/crm/customer.do?id=13 显然 id=13 的客户将被删除掉。
有开发者建议采用 id 值不要使用自增长型,而改用其他型,比如 hashcode 等。这也不大合适,可以使用爬虫轻松地将漏洞爬出来。
显然,仅仅通过界面层次控制数据级权限是不够的。
显然,需要为业务系统构建一道数据级防火墙。该防火墙能够判断当前用户对请求的数据,是否有操作权限。如果没有操作权限,告知业务系统,且告知拒绝理由。
后台业务方法被执行前,需要做个权限检查。检查通过后,才执行该操作,否则不予执行。
这种注入模式有 2 种, 1 ,硬编码注入; 2 , AOP 方法拦截。
硬编码注入,类似这样:
… if( SecurityService.permit( user, customer, Privilege.DELETE_CUSTOMER ) ) { customerService.delete( customer ); } else { … // 对该客户没有删除权限,转移到提示界面 } …
AOP 方法拦截,通过给业务方法 annotation 签名。当该方法被执行时,自动触发权限判断逻辑。
public interface CustomerService { /** * @security id=DELETE_CUSTOMER */ public void delete( Customer customer, User user ); }
大多业务系统,采用 if else 逻辑判断,判定用户是否具有对某数据的操作权限。
这种细粒度的权限逻辑,无法或者说非常难使用某种模型来自动处理。因此大多系统采取硬编码模式实现权限逻辑。
本章介绍怎样使用 Metadmin 来管理权限逻辑。
以下演示来自 metadmin 下载包里面包含的演示示例,可以在 www.metadmin.com 下载 Metadmin 安装程序包。
Metadmin 不通过编程,而是通过设计器快速设计出权限逻辑,并可在线测试权限逻辑。
MetadminService 类,提供如下方法:
|
|
表示对于 privilegeId 操作, user 对于 businessData 业务数据是否有操作权限。 context 表示权限判定需要的业务数据,可以为 null 。
WebMetadminService 类,提供如下方法:
|
|
|
|
WebMetadminService 为 MetadminService 为 Web 程序封装,从 HttpServletRequest 里面取出 user ,而不用显式地传值。
完整 JAVADOC 请浏览: http://www.metadmin.com/doc/javadoc/index.html
示例程序,对删除员工进行权限判定代码如下:
if (WebMetadminService.permit(req, Privilege.DELETE_EMPLOYEE, employee)) { employeeManager.deleteEmployee(id); }
employee 对象,是根据前台传入 id 参数从数据库查询出来的员工对象。
上章例子设置:总公司用户能查询所有员工;分公司用户能查询本分公司及下属营业部员工;营业部员工能查询本营业部员工。
本例将员工删除权限修改一下:所有用户只能删除自己所在机构的员工,也就是总公司用户只能删除总公司员工;分公司用户只能删除分公司员工;营业部用户只能删除营业部员工。
Metadmin 对于这样的权限逻辑,只要新建“所有用户”用户分类,及“用户所在机构员工”业务数据分类,然后将该 2 种分类配对设置该“删除员工”权限即可。
首先,创建“所有用户”用户分类,打开设计器: http://localhost:8080/mydemo/metadmin/designer
1. 在用户分类栏,新建分类,输入名称为“所有用户”;
2. 创建固定值 a ,设置其值为 1 ;
3. 在表达式定义处,定义 Binary 表达式“ a=a ”。
这样,所有用户都会满足该条件。
然后,创建“用户所在机构员工”业务数据分类,打开设计器: http://localhost:8080/mydemo/metadmin/designer
1. 在业务数据分类栏,新建分类,输入名称为“用户所在机构员工”;
2. 创建用户属性变量“ userCompanyId ”,其值等于用户的 companyId 属性;
3. 创建业务数据变量“ businessDataCompanyId” ,其值等于 Employee 对象的 companyId 属性;
4. 在表达式定义处,定义 Binary 表达式 userCompanyId==businessDataCompanyId 。
这样,只有当 user 对象和 employee 对象的 companyId 属性相等时,数据才属于该业务数据分类。如图示:
然后,将该分类配对设置给删除员工权限。如图示:
至此,权限逻辑设置完毕。业务代码里面集成的权限判断,将按照该逻辑执行。当权限判断逻辑发生变化,只要打开设计器重新设计权限逻辑即可。不需要修改代码,重新发布系统。