Introduction
本文档作为 micrite 用户手册的一部分,主要描述如何设置权限,对有关关开发,实现方面的内容不做讨论。
micrite 希望借助 SS(spring security) 强大的安全控制,尽可能的解决大型项目中复杂多变的权限需求。
Elements
micrite 的权限逻辑由用户 ( User )、角色 ( Role )和授权资源 ( Authority ) 之间的关系来体现。
所有的授权都针对角色 (只有 ACL 可以对用户) ,这意味着,如果一个用户 绑定 ( bind ) 到一个角色,哪他就拥有了角色上的所有授权资源 ( Authority ),
同样,如果一个用户与角色 绑除帮定 ( unbind ) ,哪他就失去了此角色所拥有的所有权限。
User,Role,Authority三者之间的关系为多对多的关系,并且可以互操作,如下图:
# User <----
# \ (bind)
# (unbind) \
# ----> Role <----
# \ (bind)
# (unbind) \
# ----> Authority
micrite 提供灵活的前台页面供用户进行 bind/unbind 操作:
| bind\unbind | to many Users | to many Roles | to many Authorities |
| from one User | N/A | 1. 菜单进入 User List 2. 选定 User 3. 点击 Bind Role 按钮 4. 在弹出的窗口中选择要操作的 Role 5. 点击 Bind 或 Unbind 按钮 | N/A |
| from one Role | 1. 菜单进入 Role List 2. 选定 Role 3. 点击 Bind User 按钮 4. 在弹出的窗口中选择要操作的 User 5. 点击 Bind 或 Unbind 按钮 | N/A | 1. 菜单进入 Role List 2. 选定 Role 3. 点击 Bind Authority 按钮 3. 在弹出的窗口中选择要操作的 Authority 3. 点击 Bind 或 Unbind 按钮 |
| from one Authority | N/A | 1. 菜单进入 Authority List 2. 选定 Authority 3. 点击 Bind Role 按钮 4. 在弹出的窗口中选择要操作的 Role 5. 点击 Bind 或 Unbind 按钮 | N/A |
Type
micrite 提供4中权限控制途径:
- Menu : 控制入口模块。
- URL Interceptor : 对指定的URL资源进行保护。
- Method Interceptor : 对指定的方法进行保护。
- Access Control List : 对特定的数据进行保护。
上面的4中授权方式的侧重点和用途各不相同,Menu 和 URL 可以通过界面被用户很容易的使用, Method 和 ACL 则需要了解 micrite 的代码和数据结构才能掌握。
Menu
micrite 的系统菜单控制着用户可以访问的入口模块。它本质上也是一个 URL,数据存储在 authorities 表,类型 ( TYPE ) 为 URL。
如果从数据库客户端执行下面 SQL 语句:
select * from authorities where type='URL' and name like '/%'
会得到如下系统默认保护的 URL:
| ID | NAME | TYPE | VALUE |
| 2 | /Security Modules/User List | URL | /security/userList.js* |
| 3 | /Security Modules/Authority List | URL | /security/authorityList.js* |
| 4 | /Security Modules/Role List | URL | /security/roleList.js* |
| 5 | /CRM Modules/Customer List | URL | /crm/customerList.js* |
- NAME:micrite 约定如果 name 字段以 ' / ' 开头,即是一个受保护的 URL ,又是一个菜单数据。最后一个 ' / ' 之后的字符串为菜单上最终显式的名称(叶子节点), 两个 ' / ' 之间的字符串都是菜单的中间节点。
- TYPE:所有的菜单都是一个受保护的 URL 。
- VALUE:模块文件的路径,末尾的 * 用来解决一些带参数的URL加载,如:'/security/userSelect.jsp?roleId=1' 。
所以,如果要为菜单上新增一个模块,需要下面几个步骤:
- 进入 Authority List 模块
- Toolbar 上的 Action Menu 菜单,点击 Add Authority
- 填写 name (注意要以' / '开头) , value, 选择 Type 为 URL
- 保存
- 为新增的菜单 bind 到相应的 Role ,否则无法生效(见 Elements 小节)。
- 重新登录,就可以看到新的菜单
URL Interceptor
普通 URL 保护的目的是为了防止他人以非法的方式获取、甚至修改数据。
例如 Authority 的第一条资源 (ID=1 ,Name='All', VALUE=/**) 表示所有的URL都被保护,系统默认将它被绑定到所有角色,这样非登录用户将不能访问 micrite 的任何 URL。
URL可以保护包括目录、图片、action等任何来自客户端请求的地址。
增加一个URL的操作方法和为菜单新增一个模块相同,注意不要在 Name 的第一个字符用 ' / '。
Method Interceptor
我们通过方法拦截来实现不同角色可以有不同的操作,也就是说一个方法只能有指定的角色才能调用。
例如 micrite 初始设置:除了 管理员组 ( ROLE_ADMIN ) 可以修改权限,普通用户 ( ROLE_USER ) 只能浏览权限。 ROLE_ADMIN 可以通过系统界面随时修改当前权限。
如果从数据库客户端执行下面 SQL 语句
select * from AUTHORITIES where TYPE='METHOD'
会得到如下系统默认保护的方法:
| ID | NAME | TYPE | VALUE |
| 6 | Role List Search Method protect | METHOD | * org.gaixie.micrite.security.service.IRoleService.*PerPage(..) |
| 7 | Role Bind Method protect | METHOD | * org.gaixie.micrite.security.service.I*Service.*bind*(..) |
| 8 | Role unBind Method protect | METHOD | * org.gaixie.micrite.security.service.I*Service.*unBind*(..) |
| 9 | add action of security module protect | METHOD | * org.gaixie.micrite.security.service.I*Service.add*(..) |
| 10 | update action of security module protect | METHOD | * org.gaixie.micrite.security.service.I*Service.update*(..) |
| 11 | delete action of security module protect | METHOD | * org.gaixie.micrite.security.service.I*Service.delete*(..) |
| 12 | update me method protect | METHOD | * org.gaixie.micrite.security.service.IUserService.updateMe(..) |
基于方法的授权资源 Type 为 METHOD, value 需要满足 aspectj 表达式,简单说明它们的作用:
ID=6 : 保护 IRoleService 接口类所有以 PerPage 结尾的方法,并忽略方法的参数。(被默认绑定到所有角色)。
ID=7,8,9,10,11 : 保护所有 Security 包下数据更新的方法,并忽略方法的参数。(被默认绑定到 ROLE_ADMIN )。
ID=12 : 保护 IRoleService 接口类的 updateMe 方法,并忽略方法的参数。(被默认绑定到所有角色)。
通过将上边保护的方法绑定到相应的角色,micrite 实现了前面提到的安全策略,ROLE_USER 下的用户除了可以修改自己的用户信息,不能作任何数据更新的操作。
有关aspectj表达式语法,可参见:Spring AOP的Reference。
micrite 使用 Spring AOP 拦截所有服务层方法 (所有 service包下以 Service结尾的类的任何方法),同时按下面流程进行处理:
# 验证方法是否被保护---->
# \ \ 否
# \ \
# \ 是 ----> 放行该方法
# \
# ---->,判断User是否有权限---->
# \ \是
# \ \
# \否 ----> 放行该方法
# \
# ----> 禁止执行该方法,给出无权限访问的提示信息
添加一个 Method 和 URL 的添加方法没有区别,注意 aspectj 表达式语法,不合法的表达式可能导致 Web Server*无法启动的严重错误。
Access Control List
作为一个示例,micrite 只对 Role 对象应用了 ACL ( Access Control List ),如果想保护更多的对象实例,需要对代码进行少量的修改,详见:。
要了解 ACL 的功能,最简单的方法就是分别用系统初始的两个用户登录,查询 Role List,会发现:admin 用户可以看到所有的 Role,
而 user 只能看到自己所在的 Role (即ROLE_USER)。
当你新增一个 Role,例如:ROLE_SALES,那么除了 ROLE_SALES 角色下的用户,其它人都无法读取此角色(ROLE_ADMIN出外)。
这样,micrite 就实现了每个用户可以浏览自己拥有的权限,管理员可以维护所有角色的权限。
目前 micrite 中 ACL 服务的处理流程如下:
# 验证方法是否被保护---->
# \ \ 否
# \ \
# \ 是 ----> 放行该方法
# \
# ---->,判断User是否有权限---->
# \ \否
# \ \
# \是 ----> 禁止执行该方法,给出无权限访问的提示信息
# \
# ----> 执行方法并获的对象实例集合---->是否调用ACL服务---->
# \ \否
# \ \
# \是 ---->返回全部结果集
# \
# ---->根据ACL配置,返回有访问权限的结果集
通过将 afterInvocationManager 加入到方法拦截后的处理链(见 applicationContext-security.xml 文件),实现了方法调用后的ACL对象结果集过滤。
注意 afterAclCollectionRead 需要一个名为 AFTER_ACL_COLLECTION_READ 的角色来配合它进行结果集过滤,角色 AFTER_ACL_COLLECTION_READ 需要
绑定一个被保护的方法(ID=6 见Method小节),如果返回的是Role对象实例结果集,ACL服务会进行过滤处理。所以不要修改或者删除此角色。
ACL 在项目中的应用,需要明确的权限划分,否则很有可能日后出现权限混乱、难以维护、效率下降的问题。
Reference
svn上checkout下来的代码看到,Customer与Usuer是一对一的吗?Cusomer不一定会指派给一个用户吧?也许是多个!
劳动成果不少,继续努力吧。