三、数据表设计
按照RBAC模型,数据库可以这样设计:
1、产品表(t_product_info)
字段名称 | 字段 | 类型 | 备注 |
产品Id | pro_id | int(11) | 自增 |
产品名称(英) | name_en | varchar(50) | not null |
产品名层(中) | name_ch | varchar(50) | not null |
创建人 | creator | varchar(50) | not null |
所属人 | owner | varchar(50) | not null |
描述 | description | varchar(255) | |
创建时间 | create_time | timestamp | not null |
2、产品成员表(t_product_member)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
产品ID | pro_id | int(11) | fk:t_produck_info |
成员ID | member_id | int(11) | not null |
创建时间 | create_time | timestamp | not null |
3、用户信息表(t_user_info)
字段名称 | 字段 | 类型 | 备注 |
用户ID | user_id | int(11) | not null |
英文名 | nike_name | varchar(10) | not null |
中文名 | real_name | varchar(10) | not null |
创建时间 | create_time | timestamp | not null |
更新时间 | update_time | timestamp | not null |
4、用户角色表(t_user_role)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
用户ID | user_id | int(11) | not null |
角色ID | role_id | varchar(50) | not null |
创建时间 | create_time | timestamp | not null |
5、角色表(t_role)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
角色ID | role_id | varchar(50) | not null,比如:A~USER |
角色名称 | role_name | varchar(50) | not null |
对象 | object | varchar(50) | not null |
对象ID | object_id | varchar(50) | not null |
角色备注 | comment | varchar(255) | |
创建时间 | create_time | timestamp | not null |
6、基础角色表(t_role_base)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
角色ID | role_id | varchar(50) | not null,比如:A~USER |
角色名称 | role_name | varchar(50) | not null |
角色备注 | comment | varchar(255) | |
权限 | permission | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
7、角色权限表(t_role_permission)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
角色ID | role_id | varchar(50) | fk:t_role->role_id |
权限 | permission | varchar(255) | not null |
基础角色ID | role_base_id | varchar(50) | fk:t_role_base->role_id |
创建时间 | create_time | timestamp | not null |
8、用户组表(t_user_group,可选)
字段名称 | 字段 | 类型 | 备注 |
组ID | id | int(11) | 自增 |
组名称 | group_name | varchar(50) | not null |
组描述 | group_desc | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
9、组角色表(t_user_group_role,可选)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
组ID | role_id | varchar(50) | not null |
角色ID | role_id | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
10、用户权限表(t_user_permission,可选)
字段名称 | 字段 | 类型 | 备注 |
记录ID | id | int(11) | 自增 |
用户ID | role_id | varchar(50) | not null |
权限 | permission | varchar(255) | not null |
创建时间 | create_time | timestamp | not null |
四、角色及权限点设计
权限控制的整个过程可以描述为:“谁”对“什么”进行什么”操作",从而,引出我们需要做的工作有:角色设计,资源定义,以及对资源的操作定义。再详细描述下,鉴权就是根据用户身份(角色)获得其对那些资源,可以进行什么操作,其中对资源的操作做为一个独立的权限体。
4.1、定义系统中的用户角色
一般是采用“通用角色+实例角色”的模式,实例角色可继承通用角色,从而拥有通用角色的权限。
常见的通用角色定义:ADMIN、MANAGER、MEMBER、GUEST 常见角色权限分配:1)SUPER_ADMIN,具有系统一切权限 1)产品ADMIN,具有当前产品所有权限;2)产品MANAGER,不具备删除权限,可修改,添加成员等 3)产品MEMEBER,可查看,修改信息,不可添加成员;4)产品GUEST,只可查看
实例角色:实例角色一般可以这样定义:“资源点+通用角色+资源ID”
注:其中资源可能是产品,可能是页面,也可能是菜单等
4.2、定义系统中的资源以及操作
一般系统中的最常见资源就是:产品(P) 一般对资源的主要操作包括:增加(CREATE)、删除(DELETE)、修改(EDIT)、查看(VIEW)
当然,系统中的资源肯定不止产品,同时产品这个粒度有些太粗,还可以更细化控制,当然一切都根据实际业务需求情况定义相应的资源点和操作。
4.3、权限体策略
权限控制策略采用五元组,如下:
资源:操作:实例:BU:密级
其中,资源可以是人,也可以是一个需求,一个文件等等;操作为对资源的操作;实例极为产品ID或者用户ID;BU,密级用于控制不同BU,不同保密模块的可见性
五、身份认证加权限管理实施
JAVA可以采用SHIRO框架,一个最简单的一个Shiro应用:1)应用代码通过subject授权,而subject又委托给SecurityManager;2)我们需要给Shiro的security注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断;
Shiro的最主要要做的工作其实就是两个:身份认证和权限校验,下面分别进行介绍。
5.1、身份认证
通过前面的文章分析,我们知道自定义身份校验校验逻辑,只需要继承AuthenticatingRealm即可,Override如下接口进行身份认证:
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}
上面这个接口,参数AuthenticationToken,它可以自定义子类实现,框架提供的有:UsernamePasswordToken,CasToken。
如果是采用用户名密码方式登陆,那么就构造一个UsernamePasswordToken,然后取数据查询用户名密码是否有效;如果是采用的CAS方式登陆,那么就通过ticket构造一个CasToken,然后与CAS服务交互验证ticket是否有效。如果验证通过会生成一个AuthenticationInfo。此时身份认证完成。
最简单的用户密码登陆身份校验代码 CAS方式验证首先得有CAS系统,这里就不说明CAS方式怎么验证了,说一下怎么用用户密码登陆进行身份校验,认证流程都一样
自定义一个AuthenticatingRealm:
public class MyRealm1 implements AuthenticatingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); //得到用户名 String password = new String((char[])token.getCredentials()); //得到密码 //取数据库中看用户名是否有效 //checkUserInfo(); //如果身份认证验证成功,返回一个AuthenticationInfo实现; return new SimpleAuthenticationInfo(username, password, getName()); } }
5.2、权限校验
权限校验主要要做的事情就是完成"从数据库中查出用户所拥有的所有权限是否包含当前待校验的权限"这么一个判断过程,因此主要要做的就是:1)从数据库中查出用户所拥有的所有权限;2)解析权限,看看是否包含待校验的权限。
1、第一步:获取用户权限信息 获取用户权限信息这个过程是在Realm中完成的,继承AuthorizingRealm,然后Override如下两个接口获取用户的权限信息:
//获取用户身份信息,Authorization前需要先获取用户身份信息 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){} //获取用户权限信息 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){ }
权限信息查询过程一般为:1)从数据库中读区用户自身所配权限;2)从数据库中读取用户角色所用拥有的权限(角色包含实例角色和BASE角色) 3)用户最终的权限:用户自身权限+用户角色权限
2、第二步:权限校验
1)如果通过角色校验,则调用hasRole,与传入的角色比较即可;
2)如果通过权限体校验,则调用isPermitted(...),与传入的权限进行比较即可。
shiro内部逻辑如下:首先通过PermissionResolver将权限字符串转换成相应的Permission实例,默认使用WildcardPermissionResolver,即转换为通配符的WildcardPermission;接着调用Permission.implies(Permission p)逐个与传入的权限比较,如果有匹配的则返回true,否则false。