使用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用户组还不在此架构范围内。
使用SpringSecurity3实现RBAC权限管理的更多相关文章
- RBAC权限管理模型 产品经理 设计
RBAC权限管理模型:基本模型及角色模型解析及举例 | 人人都是产品经理http://www.woshipm.com/pd/440765.html RBAC权限管理 - PainsOnline的专栏 ...
- Spring Security实现RBAC权限管理
Spring Security实现RBAC权限管理 一.简介 在企业应用中,认证和授权是非常重要的一部分内容,业界最出名的两个框架就是大名鼎鼎的 Shiro和Spring Security.由于Spr ...
- yii2 rbac权限管理学习笔记
下面介绍一个 yii2 的 Rbac 权限管理设置,闲话少说,直接上代码, 1.首先我们要在组件里面配置一下 Rbac ,如下所示(common/config/main-local.php或者main ...
- vue基于d2-admin的RBAC权限管理解决方案
前两篇关于vue权限路由文章的填坑,说了一堆理论,是时候操作一波了. vue权限路由实现方式总结 vue权限路由实现方式总结二 选择d2-admin是因为element-ui的相关开源项目里,d2-a ...
- ThinkPHP中RBAC权限管理的简单应用
RBAC英文全称(Role-Based Access Controller)即基于角色的权限访问控制,简单来讲,一个用户可以拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色-权限”的授 ...
- PHP中RBAC权限管理
1.RBAC概念和原理 RBAC:全称叫做Role-Based Access Control,中文翻译叫做基于角色的访问控制.其主要的作用是实现项目的权限控制. ...
- 基于RBAC权限管理的后台管理系统
在摸爬滚打中渐渐理解了RBAC权限管理是个什么玩意. RBAC的基本概念: **RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构成了访问权限三元 ...
- spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)
一,用数据库实现权限管理要注意哪些环节? 1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息, 避免页面上显示用户昵称时需要查数据库 2,如果需要在页面上显示 ...
- RBAC权限管理
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联. 简单地说,一个用户拥有若干角色,每一个角色拥有若干权限. 这样,就构造成“用户-角 ...
- Yii2-admin RBAC权限管理的实现
原文地址:http://www.open-open.com/lib/view/open1434638805348.html http://wlzyan.blog.163.com/blog/stat ...
随机推荐
- NEWSTAR PWN WEEK1
ret2text 一个简单的栈溢出 栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变.这种问题是一种特定的缓冲区溢出漏洞,类似的还有 ...
- 【YashanDB知识库】YMP元数据阶段二报错YAS-04204
[问题分类]YMP迁移 [关键字]YMP迁移,YAS-04204 [问题描述]数据库采用最小规格部署,机器配置2C8G,使用YMP进行数据和对象迁移,在元数据阶段二创建索引时报错:YAS-04204 ...
- 分布式缓存应用场景与redis持久化机制
redis 参考目录: 生产级Redis 高并发分布式锁实战1:高并发分布式锁如何实现 https://www.cnblogs.com/yizhiamumu/p/16556153.html 生产级Re ...
- JavaScript – Web Worker
前言 在上一篇 << 单线程 与 执行机制 >> 中, 我们提到了 Web Worker. 它的诞生是为了解决 JS 主线程执行耗时计算时, 导致 UI 无法及时更新的卡死现象 ...
- Java SE 23 新增特性
Java SE 23 新增特性 作者:Grey 原文地址: 博客园:Java SE 23 新增特性 CSDN:Java SE 23 新增特性 源码 源仓库: Github:java_new_featu ...
- Spring —— 注解开发(依赖注入)
自动装配 引用类型 简单类型 加载properties文件
- AE cc 2017 和 2018 中英文切换的方法
AE cc 2017中文切换英文的方法 找到AE的安装文件目录下的"Support Files"文件夹,路径为 C:\Program Files\Adobe\Adobe After ...
- 自定义 AuthenticationProvider ,UserDetailsService的实现类@Autowired 为null
项目场景: 整合spring security OAuth2自定义AuthenticationProvider 登录认证 签发token 问题描述: 在自定义 AuthenticationProvid ...
- 【赵渝强老师】在Spark SQL中读取JSON文件
Spark SQL是Spark用来处理结构化数据的一个模块,它提供了一个编程抽象叫做DataFrame并且作为分布式SQL查询引擎的作用.为什么要学习Spark SQL?如果大家了解Hive的话,应该 ...
- WPF-超市管理系统
1. 新建 WPF APP (net framework )项目