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

简介

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

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

数据库设计

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

实现RBAC权限管理的心路历程_第1张图片
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;
    }
}

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


实现RBAC权限管理的心路历程_第2张图片

模块:

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权限管理的心路历程)