美文网首页权限RBAC
实现RBAC权限管理的心路历程

实现RBAC权限管理的心路历程

作者: JerryL_ | 来源:发表于2017-05-11 21:51 被阅读0次

    简介

    RBAC是基于角色的权限控制,让角色绑定权限,用户绑定角色,它们之间都是多对多的关系。

    用户和角色好理解,但是权限究竟应该怎么标示,可以抽象成一句话来概括:谁在什么地方能进行什么操作。
    什么地方可以对应模块,什么操作可以对应模块开放的接口。

    数据库设计

    权限控制分为三个实体,角色、用户、模块
    简要的E-R模型

    E-R模型

    user和passport分开是为了逻辑更清晰,因为登陆只需要验证账号密码等账号相关的信息,可以只对一个表进行操作。
    账号和角色多对多,没问题。
    角色、模块、操作共同构成的关系就是权限。表述了:谁(角色)在什么地方(模块)能够做什么(操作)

    使用aop做全局的权限控制

    关于可配置的思考
    角色和权限的绑定肯定需要动态的,也就是可以运行时配置的。

    但是模块和操作的新增不一定能够做到可配置,因为操作和模块都是需要对应到代码的。比如,需要新增一个订单管理模块,肯定是需要编码,编写相应接口,然后才能够工作的。必须要先有这个模块的实现,才能在数据库中进行配置访问权限,这是理所当然的。因此,模块的新增不必在程序运行时配置。

    还有一个问题,怎么将数据库中action表的记录对应到代码的方法上。我采用了注解。
    权限注解:

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Privilege {
        ModuleType moduleType();//模块
        ActionType actionType();//操作
    }
    

    操作:

    public enum ActionType {
        ADD(1),EDIT(2),DEL(3),SHOW(4);
        int value;//对应数据库中操作的id
    
        ActionType(int value){
            this.value = value;
        }
    
        public int getValue(){
            return value;
        }
    }
    

    数据库中存储的操作记录:



    模块:

    public enum ModuleType {
        PRIVILEGE_MANAGEMENT(1);
    
        int value;
        ModuleType(int value){
            this.value = value;
        }
        public int getValue(){
            return value;
        }
    }
    

    数据库中的模块记录:

    全局权限控制的切面:

        @Around("@annotation(privilege)")
        public Object checkPrivilege(ProceedingJoinPoint joinPoint
                , Privilege privilege) throws Throwable {
            SessionUser user = (SessionUser) SessionUtil.getCurrentSession().getAttribute("user");
            if (user==null){
                throw new RuntimeException("haven't login yet!");
            }
            if(!privilegeService.checkPrivilege(user
                    , privilege.moduleType().getValue()
                    , privilege.actionType().getValue())){
                throw new RuntimeException("access denied!");
            }
            return joinPoint.proceed();
        }
    

    拦截注解@Privilege,根据其提供的模块信息和操作信息进行权限校验。

    使用:
    编写模块,并通过注解标识其方法对应的操作:

    @RestController
    public class TestController {
        @RequestMapping("add")
        @Privilege(moduleType = ModuleType.PRIVILEGE_MANAGEMENT
                ,actionType = ActionType.ADD)
        public Object testAdd(){
            return "add ok!";
        }
    }
    

    配置权限:


    sys_role_actions

    actions字段适当冗余减少记录数。

    权限控制流程:
    访问testAdd方法,被切面拦截,进入checkPrivilege方法,在该方法中,通过testAdd方法上的@Privilege注解,获取到testAdd方法对应的操作和模块,同时获取当前sesssion会话中的用户角色,根据角色、模块查询数据库,获取到能够进行的操作,与当前testAdd方法的操作比对,从而确定是否有权访问此方法。比对成功,继续执行,比对失败,抛出权限不足异常。

    对于这种实现的优劣分析

    优点很明显,能够做到角色权限可配置,用户角色可配置,而且也减小了权限逻辑和业务逻辑的耦合度,需要调用权限控制,只需要加上权限控制的注解即可。
    缺点也很明显,无法做到模块和操作的动态增加,因为模块和操作都是硬编码的,每次新增模块或者新增操作,势必要经过修改代码、编译、重新运行的过程。

    demo:
    https://github.com/DangerousPrayer/sso-server
    仅作参考。

    相关文章

      网友评论

        本文标题:实现RBAC权限管理的心路历程

        本文链接:https://www.haomeiwen.com/subject/ptxotxtx.html