shiro之权限过滤

Shiro之权限过滤

1,什么是基于资源的访问控制

 

RBAC基于资源的访问控制(Resource-BasedAccess Control)是以资源为中心进行访问控制,比如:主体必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

 

总经理    查所有员工的工资信息

董事长    添加一个查看所有员工的权限

 

if(主体.拥有查看工资的权限){

    //查看工资

}

 

 

上图中的判断逻辑代码可以理解为:

if(主体.hasPermission("查询工资权限标识")){

         查询工资

}

 

改代码  从新编译  从新部署

 

优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。

2权限模型

 

主体(当前用户)                            sys_user

资源(资源名称、访问地址)   sys_office

权限(权限名称、资源id)          

角色(角色名称)                            sys_role

角色和权限关系(角色id、权限id)sys_role_office

主体和角色关系(主体id、角色id)sys_user_role

 

 

如下图:

 

 

 

 

3.1本系统权限分析

 

 

 

本系统和权限有关的几张表

 

 

 

 

 

// 数据范围(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<String>dataScope = Lists.newArrayList();

       

        // 超级管理员,跳过权限过滤

        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"";

    }

3.2.1关于一个用户多个角色的问题

是按照权限累加原则入上面的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

 

3.2.2关于一个用户最终权限的问题

    public static Integer getUserDataScope(){

   

    User user = UserUtils.getUser();

    user =user.getCurrentUser();

    List<Role> listRole=user.getRoleList();

    List<String> roeNames=new ArrayList<String>();

    // 数据范围dataScope1:所有数据;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;

    }  

 

 

 

 

 

 

 

3.2.3关于跨部门派单问题

需要这个用户的角色权限按明细设置,并选中所跨部门

 

 

 

 

 

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                        表中得到该用户的信息

 

      <!-- 根据OfficeId role获取用户(树查询用户时用) -->

         <selectid="findUserByOfficeIdAndRole" resultType="User"useCache="true">

                   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

         </select>

 

data_scope='9'按明细设置和其他权限的分离,如果不分离而且这个用户拥有的另一个角色的权限混淆,  假设一个用户拥有一个所有权限的角色,那么每个部门都会查到该用户,因为我们查找的是拥有该部门权限的用户,这样显然不是我们想要的跨部门处理

 

 

3.2.4关于权限递减分配问题

如果你给了一个用户的权限,他再创建角色的时候只能分配小于等于当前权限

 

数据范围(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<Dict> getDictListRange(String type){

        @SuppressWarnings("unchecked")

        Map<String,List<Dict>> dictMap = (Map<String, List<Dict>>)CacheUtils.get(CACHE_DICT_MAP);

        IntegeruserDataScope = UserUtils.getUserDataScope();      

        if (dictMap==null){

            dictMap= Maps.newHashMap();

            for (Dict dict :dictDao.findAllList(new Dict())){

                List<Dict>dictList = dictMap.get(dict.getType());

               

                    if (dictList !=null){

                        dictList.add(dict);

                    }else{

                        dictMap.put(dict.getType(),Lists.newArrayList(dict));

                    }

            }

            CacheUtils.put(CACHE_DICT_MAP, dictMap);

        }

        List<Dict>dictList = dictMap.get(type);

        if (dictList ==null){

            dictList= Lists.newArrayList();

        }

        List<Dict>dictListRange=new ArrayList<Dict>();

        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;

    }

   

4关于用户仅自己数据和仅部门数据权限下添加用户和机构的问题

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'/>"/>&nbsp;

          

           </c:if>,

2,如果是仅自己的话不让添加用户和角色

5关于用户仅按明细设置问题

这个问题和角色授权同样的做法,添加的角色也只能按明细设置,范围是当前用户所以拥有的范围(可以在数据库中查到)

或者这样做   如果该用户拥有所有数据的权限,才有  权限按明细设置的分配,

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
web.xml配置 因为我们是与spring进行集成的,而spring的基本就是web项目的xml文件。所以我们在web.xml中配置shiros的过滤拦截。正常情况下,我们需要将shiro的filter配置在所有的filter前面,当然和encodingFilter这个filter是不区分前后的。因为两者互相不影响的。spring-shiro.xml 这里我们将来看看spring-shiro.xml的配置,这里我采取倒叙的方式讲解,我觉的倒叙更加的有助于我们理解代码。首先我们还记得在web.xml中配置的那个filter吧,名字shiroFilter,对spring-shiro.xml配置文件就是通过这个filter展开的。首先我们在web.xml配置的过滤器实际上是配置ShiroFilterFactoryBean,所以在这里需要将ShiroFilterFactoryBean定义为shiroFilter <!-- Shiro的核心安全接口,这个属性是必须的 --> <!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.html"页面 --> <!-- 登录成功后要跳转的连接 --> <!-- 用户访问未对其授权的资源时,所显示的连接 --> <!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp --> <!-- Shiro连接约束配置,即过滤链的定义 --> <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 --> <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 --> <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 --> <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter --> /statics/**=anon /login.html=anon /sys/schedule.html=perms[sys:schedule:save] /sys/login=anon /captcha.jpg=anon /**=authc

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值