使用SpringSecurity3实现RBAC权限管理

1、 What? 什么是权限管理?

具体可参见百度:http://baike.baidu.com/view/2108713.htm

名词备注:

数据级权限:百科内的权限管理一文解释的比较不错,但其中的“数据级权限”有的人看来会觉得有点摸不着头脑。数据级权限,即表示权限与特定数据有联系的权限,比方说,某用户只能创建100个用户。这个100,就是数据级权限的一个指标。

2、 How?怎么样实现权限管理?

2.1、一种烦恼

也许很多程序员会在权限管理中遇到这样的一个问题。

大部分项目都需要权限管理系统,但不同的项目背景中,角色的种类和对应的权限灵活多变。往往需要在维护和调研时花费大量的功夫去分析,而最后由于不同客户方不同层面的领导的意见不一或者不同的决策等问题,造成多次的翻工(也许开始你定好了适合他们的权限机制,但后来又有些不可抗拒因素导致你又要修改项目)。

这样的问题是一种无用功,而且是十分让人烦恼的。

如何去解决怎样的问题?

2.2、权限管理架构图

2.2.1、用户user

保存基本的用户名,密码,角色表id和用户状态。

管理员可以修改用户的角色。

PS:系统最基本的状态至少需要保留一个默认管理员账号。

2.2.2、权限privilege

用来判定(vote)功能及数据级权限管理的依据。

项目创建者内置的权限集合,不给与管理的权限。否则将可能造成项目功能的缺陷。

2.2.3、角色role

决定用户具体包含权限列表。

role_privilege连接role与privilege两个表用来表示关系表连接构造many-to-many关系。

同样的,系统默认状态也必须保留至少一种角色为系统自带的管理,这个角色具备系统中全部的权限。也就是说,该角色不受role_ privilege表所限制,会直接读取privilege中的所有权限集。

2.2.4、权限分类privilege_category

为了更加完善的展现权限分配模块,可以构造一个权限分类。

2.3、用SpringSecurity3实现具体功能

具体SpringSecurity3的配置这里就不详细说明了,可参考网上其他资料。因为本文主要讲述的是如何实现RBAC权限管理模块。

    注:自定义的权限的命名必须以ROLE_ 开头,例如ROLE_USER_CREATE等。

2.3.1、权限与角色误区

使用SpringSecurity3的时候,网上很多的资料都能让你的模块跑起来。但随之而来的是一些误区。

比方说,会把权限和角色两者混淆。比较经典的例子如下:

Collection<GrantedAuthority> auths=new ArrayList<GrantedAuthority>(); GrantedAuthorityImpl roleAdmin=new GrantedAuthorityImpl("ROLE_ADMIN"); GrantedAuthorityImpl roleUser=new GrantedAuthorityImpl("ROLE_USER"); auths.add(roleAdmin); auths.add(roleUser); 

然后访问权限的配置如下:

<intercept-url pattern="/**" access="ROLE_USER" />

虽然也许看上去没什么问题,但其实存在一定的隐患。因为能访问页面的不是因为他是user,而是因为他有“访问”的权限。

如果后来增加了一个guest的角色,而他能访问系统,但不能含有user的权限。因为user的权限可能有附带很多界面上的功能,但不附上ROLE_USER的话guest又不能访问系统,所以你就不得不修改配置文件中的access。

造成这个的问题的最终原因就是角色和权限混淆了。

2.3.2、权限分配的灵活性

要想最大限度把权限分配变得灵活,角色提供可摘取权限的功能是必不可少的。而针对不同的项目背景,所有的角色和权限也许会出现各种的变化。但其功能还是离不开分配角色和分配权限。

而通过role_privilege表,我们可以在用户登录系统的时候,把该用户角色的权限通过SpringSecurity帮助我们放到用户的权限组内。从而我们可以利用SpringSecurity提供的各种标签,标注访问控制等实现权限功能管理和现实。

例子:

<sec:authorize ifAnyGranted="ROLE_CREATE,ROLE_UPDATE,ROLE_READ,ROLE_DELETE"> <a>用户管理</a> </sec:authorize>

即用户拥有OLE_CREATE,ROLE_UPDATE,ROLE_READ,ROLE_DELETE任何一个权限的时候才能查看到用户管理按钮。

点进去之后,我们可以再细分对应的权限操作,没有的权限则该功能模块会不出现。

2.3.3、权限的包含关系

有的时候,权限还必须有“包含”关系,即若你具备了某权限,则另外的权限你也必定会具备。

比方说,有个角色他有删除用户的权限,但他没有读取用户的权限。这样觉得有没有问题呢?

若用户没有读取用户的权限,连列表都不出来,那他如何实现删除?这样看上去虽然不是系统上的问题。但一个完善的系统,必须去考虑这样的情况发生。

2.3.4、自定义UserDetailsService接口实现类

import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import cn.com.timekey.drugmonitor.business.PrivilegeBus; import cn.com.timekey.drugmonitor.business.UserBus; import cn.com.timekey.drugmonitor.log.Log; import cn.com.timekey.drugmonitor.log.LogFactory; import cn.com.timekey.drugmonitor.po.Privilege; import cn.com.timekey.drugmonitor.po.Role; import cn.com.timekey.drugmonitor.po.RolePrivilege; import cn.com.timekey.drugmonitor.po.Users; /** * @author Kenny */ public class MyUserDetailsService implements UserDetailsService { private static final Log LOGGER = LogFactory .getLog(MyUserDetailsService.class); private static final String SYSTEM_ROLE_ID = "1";// 系统默认管理员的id private UserBus userBus; private PrivilegeBus privilegeBus; public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { if (StringUtils.isBlank(username)) { throw new UsernameNotFoundException("no such user.", username); } Users user = userBus.findByName(username); if (user == null) { LOGGER.debug("no such user by " + username); throw new UsernameNotFoundException("no such user.", username); } else if (user.getRole() == null) { LOGGER.debug("no such role by " + username); throw new UsernameNotFoundException("no such user.", username); } String adminName = user.getUserName(); String password = user.getUserPassword(); Role role = user.getRole(); @SuppressWarnings("unchecked") Collection<Privilege> privileges = CollectionUtils.EMPTY_COLLECTION; // 判断是否为系统默认管理员,若是,则直接获取privilege表中全部权限。 if (StringUtils.equals(role.getRoleId(), SYSTEM_ROLE_ID)) { privileges = privilegeBus.findAll(); } Set<RolePrivilege> rolePrivileges = role.getRolePrivileges(); @SuppressWarnings("unchecked") Collection<GrantedAuthority> authorities = CollectionUtils.EMPTY_COLLECTION; if (privileges.isEmpty() && rolePrivileges != null && !rolePrivileges.isEmpty()) { privileges = new ArrayList<Privilege>(rolePrivileges.size()); for (RolePrivilege rolePrivilege : rolePrivileges) { privileges.add(rolePrivilege.getPrivilege()); } } if (privileges.isEmpty()) { LOGGER.warn("user has not any rolePrivileges."); throw new UsernameNotFoundException("Privilege fail! ", username); } // 构造权限组 authorities = generateAuthorities(privileges); boolean isEnable = user.getIsActive();// 如果账号有状态的话,可根据查询结果配置该值。 return new org.springframework.security.core.userdetails.User( adminName, password, isEnable, true, true, true, authorities); } /** * 构造权限组 * * @param rolePrivileges * @return */ private Collection<GrantedAuthority> generateAuthorities( Collection<Privilege> privileges) { List<GrantedAuthority> auth = new ArrayList<GrantedAuthority>( privileges.size()); for (Privilege rolePrivilege : privileges) { GrantedAuthority authority = new GrantedAuthorityImpl( rolePrivilege.getPrivilegeName()); auth.add(authority); } return auth; } public void setUserBus(UserBus userBus) { this.userBus = userBus; } public void setPrivilegeBus(PrivilegeBus privilegeBus) { this.privilegeBus = privilegeBus; } }

2.3.5、系统的权限漏洞

出来页面上使用TAG来实现基本的权限功能隐藏与显示外。还必须注意系统内部的权限判断。

使用拦截器保护限制资源

TAG帮我们隐藏了功能的URL,但该URL还是存在的,只要对方知道URL就能直接发请求过来了,从而绕过了权限管理。

我们可以使用拦截器进一步处理请求权限的问题。具体可以在<http auto-config>代码块中配置,如:

<intercept-url pattern="/createUser.do" access="ROLE_USER_CREATE" />

<intercept-url pattern="/listUser.do" access="ROLE_USER_READ" /> <intercept-url pattern="/**" access="ROLE_LOGIN" />

解释:

访问/createUser.do资源必须有ROLE_USER_CREATE权限。

访问/listUser.do资源必须有ROLE_USER_READ。

访问任何资源都必须有ROLE_LOGIN才行。(也可以用IS_AUTHENTICATED_REMEMBERED)

使用标注保护方法调用

有的时候,程序员在编码中会出现疏忽,导致引用错方法。或者在编码时,没有考虑到权限的问题造成一些跨权限的漏洞。

比方说,某个功能因为疏忽,没在拦截器上配置权限拦截,或者功能定义了两个URL入口,而只有其中一个在拦截器上配置了(一个也许是系统旧的遗留入口)。

这样的情况下就会带来权限漏洞,有不良目的人就可以使用这些漏洞来攻击系统,但最重要的还是造成客户损失。

要更进一步的加强“保险”,我们还可以使用标注在代码里面声明拥有某种权限才能调用特定的方法(也可以使用AOP声明的方式,但个人更加喜欢标注的形式,但标注的话相对于会硬编码些)。

使用标注时,记得要在添加上下面代码才生效喔!

<global-method-security secured-annotations="enabled"> </global-method-security>

例子如:

import org.springframework.security.access.annotation.Secured; public interface AccountBusiness { @Secured("ROLE_USER_CREATE") public void save(User user); @Secured("ROLE_USER_DELETE") public void delete(String id); }

3、 Gain 我们的收获

我们再不用去考虑系统中不同角色的有什么权限的问题了,因为权限分配十分灵活化。程序员不必纠结不同项目中,什么角色应该具备哪些权限而烦恼了,但我们必须配置好一套完善的权限列表来满足用户的分配需求。虽然不完美,但减少了很多的烦恼。

角色与权限分配的功能由客户或者业务人员自己来决定。我们只要提供好足够满足对方需求的权限范围就可以了。权限缺少的时候,我们可以增加,但这些工作不至于是无用功。

4、 遗留问题

1.用户登录时,必须重新从数据库里面拿角色对应的权限集,资源消耗是否有点多?

就这个问题而言,我是觉得没必要计较这些资源消耗的,因为权限集再怎么多也不会超过50条吧。

而权限管理系统,一般并发量也不会大的了。如果真的纠结这样的消耗,也可以放用static map用来实现角色与权限集的获取,但记得用上观察者模式。因为权限集是可以被修改的,不用观察者的话就会出现得到过期的权限集了。

2.虽然考虑到数据级的权限管理问题,但目前还是没有提供这样的案例。

3.Group用户组还不在此架构范围内。

时间: 2025-01-07 10:07:10

使用SpringSecurity3实现RBAC权限管理的相关文章

RBAC权限管理

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限. 这样,就构造成“用户-角色-权限”的授权模型.在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系.(如下图) 角色是什么?可以理解为一定数量的权限的集合,权限的载体.例如:一个论坛系统,“超级管理员”.“版主”都是角色.版主可管理版内的帖子.可管理版内的用户等,这些是权限.要给某个用户授予这些权限,不需要直接

基于RBAC权限管理模型学习

在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限.这就极大地简化了权限的管理. 在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色.角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收. 角色与角色的关系可以建立起来以囊括更广泛的客观情况. BAC支持三个著名的安全原则:最小权限原则,责任分离原则和数据抽象原则. (1)最小权限原则之所以被RBAC所支持,是因

vue基于d2-admin的RBAC权限管理解决方案

前两篇关于vue权限路由文章的填坑,说了一堆理论,是时候操作一波了. vue权限路由实现方式总结 vue权限路由实现方式总结二 选择d2-admin是因为element-ui的相关开源项目里,d2-admin的结构和代码是让我感到最舒服的,而且基于d2-admin实现RBAC权限管理也很方便,对d2-admin没有大的侵入性的改动. 预览地址 Github 相关概念 不了解RBAC,可以看这里企业管理系统前后端分离架构设计 系列一 权限模型篇 实现了RBAC模型权限控制 菜单与路由独立管理,完全

ThinkPHP RBAC权限管理机制

RBAC是ThinkPHP很好用的后台权限管理的,话不多说,实现方法如下,也方便以后自己查询使用: 1.新建4个数据库表 self_role权限表 CREATE TABLE `self_role` ( `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `pid` smallint(6) DEFAULT NULL, `status` tinyint(1) unsigned DEFAULT

基于RBAC权限管理

一般web系统操作人员多时都会需求权限管理,一来限制操作范围,二来限制数据公开度. 现在最流行的一个模式为 RBAC (Role-Based Access Control) 基于角色的访问控制.设定权限范围定义到角色中,然后再分配到每个用户. 这里仅以一般后台管理系统为例,叙说数据结构: 需求: 菜单需要针对不同部门使用不同的菜单结构. 权限项能精确到页面中某个内容或局部功能. 基本要求:没有权限的菜单,页面中内容或链接禁止显示. 表结构 CREATE TABLE `power_item` (

【转】RBAC权限管理

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色-权限”的授权模型.在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系.(如下图) 角色是什么?可以理解为一定数量的权限的集合,权限的载体.例如:一个论坛系统,“超级管理员”.“版主”都是角色.版主可管理版内的帖子.可管理版内的用户等,这些是权限.要给某个用户授予这些权限,不需要直接将

Yii2-admin RBAC权限管理的实现

yii2-admin是yii2 rbac的一套管理工具,实现了漂亮的界面和完整的权限管理功能,不用自己再去写权限代码了,使用之前请将yii2的源码更新到最新版本. git源码地址:https://github.com/mdmsoft/yii2-admin 安装yii2-admin: 1.首先切换到项目目录下 2.执行该语句:composer.phar require mdmsoft/yii2-admin 注:如果提示could not open input file composer.phar

基于Django实现RBAC权限管理

概述 RBAC(Role-Based Access Control,基于角色的访问控制),通过角色绑定权限,然后给用户划分角色.在web应用中,可以将权限理解为url,一个权限对应一个url. 在实际应用中,url是依附在菜单下的,比如一个简单的生产企业管理系统,菜单可以大致分为以下几块:制造.资材.生产管理.人事.财务等等.每个菜单下又可以有子菜单,但最终都会指向一个url,点击这个url,通过Django路由系统执行一个视图函数,来完成某种操作.这里,制造部的员工登录系统后,肯定不能点击财务

thinkPHP的RBAC权限管理

thinkphp自带一个权限类:RBAC.class.php,里面有生成表的create table语句,也就是它的数据库设计,还有一些方法,比如getAccessList(),可以根据管理员ID号获得权限节点. 1.涉及到的表有五个,为了方便理解,可以总结为: 用户表(user):顾名思义,就是用户了,比如admin.张三.李四.王五 角色表(role):顾名思义,就是定义好的角色,比如财务管理员.文章管理员.产品管理员 用户角色关系表(user_role):顾名思义,就是将用户和角色对应起来