Shiro之权限过滤
RBAC基于资源的访问控制(Resource-BasedAccess Control)是以资源为中心进行访问控制,比如:主体必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:
总经理 查所有员工的工资信息
董事长 添加一个查看所有员工的权限
if(主体.拥有查看工资的权限){
//查看工资
}
上图中的判断逻辑代码可以理解为:
if(主体.hasPermission("查询工资权限标识")){
查询工资
}
改代码 从新编译 从新部署
优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。
主体(当前用户) sys_user
资源(资源名称、访问地址) sys_office
权限(权限名称、资源id)
角色(角色名称) sys_role
角色和权限关系(角色id、权限id)sys_role_office
主体和角色关系(主体id、角色id)sys_user_role
如下图:
本系统和权限有关的几张表
// 数据范围(1:所有数据;2:所在公司及以下数据;3:所在公司数据;4:所在部门及以下数据;5:所在部门数据;8:仅本人数据;9:按明细设置)
public static final String DATA_SCOPE_ALL ="1";
public static final String DATA_SCOPE_COMPANY_AND_CHILD ="2";
public static final String DATA_SCOPE_COMPANY ="3";
public static final String DATA_SCOPE_OFFICE_AND_CHILD ="4";
public static final String DATA_SCOPE_OFFICE ="5";
public static final String DATA_SCOPE_SELF ="8";
public static final String DATA_SCOPE_CUSTOM ="9";
查找部门:
dataScopeFilter()中的三个参数分别当前用户,关系表 AS之后的名字,关系表 AS之后的名字
,第三个参数可以为空在不需要更多的关系表情况之下
office.getSqlMap().put("dsf", BaseService.dataScopeFilter(user.getCurrentUser(),"a", ""));
查找用户:
user.getSqlMap().put("dsf",dataScopeFilter(user.getCurrentUser(), "o", "a"));
查找工单:
因为工单没有直接和权限发生关系,需要绕一下,就是要关联的和权限有关的东西
因为每个工单都能找到创建者的ID,所可以left join user表 AS u
JOIN sys_user u ON u.id =a.create_by
再由创建关联到所属的部门就能完成权限的过滤
LEFT JOIN sys_office off ON off.id = u.office_id
worksheet.getSqlMap().put("dsf",dataScopeFilter(user.getCurrentUser(), "off", "u"));
/**
* 数据范围过滤
* @param user当前用户对象,通过“entity.getCurrentUser()”获取
* @param officeAlias机构表别名,多个用“,”逗号隔开。
* @param userAlias用户表别名,多个用“,”逗号隔开,传递空,忽略此参数
* @return标准连接条件对象
*/
public static StringdataScopeFilter(Useruser, String officeAlias, String userAlias) {
StringBuildersqlString = new StringBuilder();
// 进行权限过滤,多个角色权限范围之间为或者关系。
List
// 超级管理员,跳过权限过滤
if(!user.isAdmin()){
boolean isDataScopeAll=false;
for (Role r :user.getRoleList()){
for (String oa :StringUtils.split(officeAlias,",")){
if(!dataScope.contains(r.getDataScope()) && StringUtils.isNotBlank(oa)){
if (Role.DATA_SCOPE_ALL.equals(r.getDataScope())){
isDataScopeAll= true;
}
elseif
//所在公司及以下数据
Sql: AND (a.id ='8a8ca7be0b82404a9a3750899b33ef58' OR a.parent_ids LIKE'0,8a8ca7be0b82404a9a3750899b33ef58,%')
(Role.DATA_SCOPE_COMPANY_AND_CHILD.equals(r.getDataScope())){
sqlString.append(" OR" + oa +".id = '" +user.getCompany().getId() + "'");
sqlString.append(" OR" + oa +".parent_ids LIKE '" + user.getCompany().getParentIds()+ user.getCompany().getId() +",%'");
}
//所在公司数据
Sql: AND (a.id ='8a8ca7be0b82404a9a3750899b33ef58' OR (a.parent_id ='8a8ca7be0b82404a9a3750899b33ef58' AND a.type = '2')
elseif (Role.DATA_SCOPE_COMPANY.equals(r.getDataScope())){
sqlString.append(" OR" + oa +".id = '" +user.getCompany().getId() + "'");
// 包括本公司下的部门(type=1:公司;type=2:部门)
sqlString.append(" OR(" + oa +".parent_id = '" +user.getCompany().getId() + "' AND " + oa + ".type ='2')");
}
elseif
//所在部门及以下数据
Sql: AND (a.id ='026e557cdb5a4016870f6938c82f9f73' OR a.parent_ids LIKE'0,8a8ca7be0b82404a9a3750899b33ef58,fca8d8cf19774811b75171bb87b65cdf,026e557cdb5a4016870f6938c82f9f73,%')
(Role.DATA_SCOPE_OFFICE_AND_CHILD.equals(r.getDataScope())){
sqlString.append(" OR" + oa +".id = '" +user.getOffice().getId() + "'");
sqlString.append(" OR" + oa +".parent_ids LIKE '" +user.getOffice().getParentIds() + user.getOffice().getId() +",%'");
}
//所在部门数据
Sql:AND (a.id ='026e557cdb5a4016870f6938c82f9f73' OR a.id IS NULL)
elseif (Role.DATA_SCOPE_OFFICE.equals(r.getDataScope())){
sqlString.append(" OR" + oa +".id = '" +user.getOffice().getId() + "'");
}
//按明细设置(EXISTS存在)
Sql: AND (EXISTS(SELECT 1 FROM sys_role_office WHERE role_id ='9d5c866445784ddd8bd63570d86fe2a6' AND office_id = a.id) OR a.id IS NULL)
elseif (Role.DATA_SCOPE_CUSTOM.equals(r.getDataScope())){
sqlString.append(" OREXISTS (SELECT 1 FROM sys_role_office WHERE role_id = '" + r.getId() +"'");
sqlString.append(" ANDoffice_id = " + oa +".id)");
}
//else if(Role.DATA_SCOPE_SELF.equals(r.getDataScope())){
dataScope.add(r.getDataScope());
}
}
}
仅本人数据:(这个语句不管怎样的权限都会有)
Sql : AND (a.id ='117d955c20114f41b0307132c52295ac')//查用户
// 如果没有全部数据权限,并设置了用户别名,则当前权限为本人;如果未设置别名,当前无权限为已植入权限
if(!isDataScopeAll){
if (StringUtils.isNotBlank(userAlias)){
for (String ua :StringUtils.split(userAlias,",")){
sqlString.append(" OR" + ua +".id = '" + user.getId()+ "'");
}
}else {
for (String oa :StringUtils.split(officeAlias,",")){
sqlString.append(" OR" + oa +".id IS NULL");
}
}
}else{
// 如果包含全部权限,则去掉之前添加的所有条件,并跳出循环。
sqlString = newStringBuilder();
}
}
if (StringUtils.isNotBlank(sqlString.toString())){
return" AND(" + sqlString.substring(4) + ")";
}
return"";
}
是按照权限累加原则入上面的list 的循环
按明细和所在部门双角色 sql:
SELECT a.id, a.parent_id AS"parent.id", a.parent_ids, a.area_id AS "area.id", a.code,a.name, a.sort, a.type, a.grade, a.address, a.zip_code, a.master, a.phone,a.fax, a.email, a.remarks, a.create_by AS "createBy.id",a.create_date, a.update_by AS "updateBy.id", a.update_date, a.del_flag,a.useable AS useable, a.primary_person AS "primaryPerson.id",a.deputy_person AS "deputyPerson.id", p.name AS"parent.name", ar.name AS "area.name", ar.parent_ids AS"area.parentIds", pp.name AS "primaryPerson.name", dp.nameAS "deputyPerson.name"
FROM sys_office a
LEFT JOIN sys_office p ON p.id =a.parent_id
LEFT JOIN sys_area ar ON ar.id = a.area_id
LEFT JOIN sys_user pp ON pp.id =a.primary_person
LEFT JOIN sys_user dp ON dp.id =a.deputy_person
WHERE a.del_flag = 0
AND (a.id ='026e557cdb5a4016870f6938c82f9f73' OR a.parent_ids LIKE'0,8a8ca7be0b82404a9a3750899b33ef58,fca8d8cf19774811b75171bb87b65cdf,026e557cdb5a4016870f6938c82f9f73,%'
OR EXISTS (SELECT 1 FROM sys_role_officeWHERE role_id = '9d5c866445784ddd8bd63570d86fe2a6' AND office_id = a.id) ORa.id IS NULL)
ORDER BY a.create_date
public static Integer getUserDataScope(){
User user = UserUtils.getUser();
user =user.getCurrentUser();
List
List
// 数据范围dataScope(1:所有数据;2:所在公司及以下数据;3:所在公司数据;
// 4:所在部门及以下数据;5:所在部门数据;8:仅本人数据;9:按明细设置)
由此我们总结到(除了9:按明细设置)权限越大数值越小,所以我们取所有角色权限中最小的数值
Integer range=100;
for (Role role : listRole) {
int roRange=Integer.parseInt(role.getDataScope());
if(range>roRange&&roRange!=9){
range=roRange;
}
roeNames.add(role.getName());
}
return range;
}
需要这个用户的角色权限按明细设置,并选中所跨部门
sql分析:sys_office sys_role_office sys_role sys_user_role sys_user
我们已知部门ID,要查找拥有该部门(按明细设置)权限的所有用户
首先应该从部门查找开始,排除已删除的,但是我们能看到得部门肯定是没删除的,所以从关系表sys_role_office查找开始,减少一次子查询
从sys_role_office 表中得到拥有该部门的角色ID
从sys_role 表中排除已删除的角色
从sys_user_role 表中找到拥有该角色的用户ID
从sys_user 表中得到该用户的信息
SELECT
a.id,a.name, a.login_name
fromsys_user a where a.id in (
(select user_id from sys_user_role where role_id in
(selectid from sys_role where id in
(selectsro.role_id from sys_role_office sro where sro.office_id= #{officeId}) and del_flag='0' and data_scope='9' and del_flag='0')))
ora.office_id = #{officeId} anda.del_flag='0'
ORDERBY a.name
data_scope='9'按明细设置和其他权限的分离,如果不分离而且这个用户拥有的另一个角色的权限混淆, 假设一个用户拥有一个所有权限的角色,那么每个部门都会查到该用户,因为我们查找的是拥有该部门权限的用户,这样显然不是我们想要的跨部门处理
如果你给了一个用户的权限,他再创建角色的时候只能分配小于等于当前权限
数据范围(1:所有数据;2:所在公司及以下数据;3:所在公司数据;4:所在部门及以下数据;5:所在部门数据;8:仅本人数据;9:按明细设置)(数值越小范围越大)
要做到权限递减分配我们需要重写从字典中获取权限下拉列表的方法
新增:
<c:iftest="${role.id ==null}">
<form:selectpath="dataScope"class="input-medium">
<form:optionsitems="${fns:getDictListRange('sys_data_scope')}"itemLabel="label"itemValue="value"htmlEscape="false"/>
form:select>
c:if>
原有:
<c:iftest="${role.id !=null}">
<form:selectpath="dataScope"class="input-medium">
<form:optionsitems="${fns:getDictList('sys_data_scope')}"itemLabel="label"itemValue="value"htmlEscape="false"/>
form:select>
c:if>
public staticList
@SuppressWarnings("unchecked")
Map
IntegeruserDataScope = UserUtils.getUserDataScope();
if (dictMap==null){
dictMap= Maps.newHashMap();
for (Dict dict :dictDao.findAllList(new Dict())){
List
if (dictList !=null){
dictList.add(dict);
}else{
dictMap.put(dict.getType(),Lists.newArrayList(dict));
}
}
CacheUtils.put(CACHE_DICT_MAP, dictMap);
}
List
if (dictList ==null){
dictList= Lists.newArrayList();
}
List
for (Dict dict :dictList) {
// 权限按明细设置的分配,如果该用户拥有所有数据的权限,
那么明细按设置可以给他
if(Integer.parseInt(dict.getValue())==9&&userDataScope==1){
dictListRange.add(dict);
}
// 权限仅本人数据的分配,如果该用户不是只拥有自己的的权限,
仅本人数据可以给他
else if(Integer.parseInt(dict.getValue())==8&&userDataScope!=8){
dictListRange.add(dict);
}
//小于等于该用户的权限可以给他 if(userDataScope>Integer.parseInt(dict.getValue())||Integer.parseInt(dict.getValue())==8||Integer.parseInt(dict.getValue())==9){
continue;
}else{
dictListRange.add(dict);
}
}
return dictListRange;
}
1,如果是仅自己和仅部门的话不让添加机构
OfficeController.java
model.addAttribute("range",UserUtils.getUserDataScope());//获得该用户的最终权限
offficeForm.jsp 中屏蔽保存按钮
<c:iftest="${range!=5 && range!=8 }">
<inputid="btnSubmit" onclick="returncheckForm()"class="btnbtn-primary"type="submit"value="<spring:messagecode='public_btn_save'/>"/>
c:if>,
2,如果是仅自己的话不让添加用户和角色
这个问题和角色授权同样的做法,添加的角色也只能按明细设置,范围是当前用户所以拥有的范围(可以在数据库中查到)
或者这样做 如果该用户拥有所有数据的权限,才有 权限按明细设置的分配,