简介
RBAC是基于角色的权限控制,让角色绑定权限,用户绑定角色,它们之间都是多对多的关系。
用户和角色好理解,但是权限究竟应该怎么标示,可以抽象成一句话来概括:谁在什么地方能进行什么操作。
什么地方可以对应模块,什么操作可以对应模块开放的接口。
数据库设计
权限控制分为三个实体,角色、用户、模块
简要的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!";
}
}
配置权限:
actions字段适当冗余减少记录数。
权限控制流程:
访问testAdd方法,被切面拦截,进入checkPrivilege方法,在该方法中,通过testAdd方法上的@Privilege注解,获取到testAdd方法对应的操作和模块,同时获取当前sesssion会话中的用户角色,根据角色、模块查询数据库,获取到能够进行的操作,与当前testAdd方法的操作比对,从而确定是否有权访问此方法。比对成功,继续执行,比对失败,抛出权限不足异常。
对于这种实现的优劣分析
优点很明显,能够做到角色权限可配置,用户角色可配置,而且也减小了权限逻辑和业务逻辑的耦合度,需要调用权限控制,只需要加上权限控制的注解即可。
缺点也很明显,无法做到模块和操作的动态增加,因为模块和操作都是硬编码的,每次新增模块或者新增操作,势必要经过修改代码、编译、重新运行的过程。
demo:
https://github.com/DangerousPrayer/sso-server
仅作参考。