因为最近一直卡在权限控制这个坎上,原来设计的比较简单的权限控制思路已经没法知足比较复杂一些的场景,所以一直在探索一种在大部分场景下比较通用的权限模型。html
首先,这里说明一下两种RBAC权限模型分别是“基于角色的权限控制(Role-Based-Access-Control)”和“基于资源的权限控制(Resource-Based-Access-Control)”两种模型,这两种模型是Java最多见的权限控制的模型。它们之间的数据库结构区别并无太大,甚至也能够同样。都是以最基础的五张表(用户表、角色表、用户-角色关系表、权限表、角色-权限关系表)组成,日后再复杂的业务再在这个基础上进行拓展,例如加入用户组、组织、模块等等概念,能够参考一下一这篇文章:java
https://blog.csdn.net/qiaqia609/article/details/38102091
数据库
下面就以Spring Boot + Shiro为载体来具体记录一下。apache
这里仍是贴一下Shiro的配置文件:ShiroConfig.javaapi
-
/**
-
* @author phw
-
* @date Created in 04-08-2018
-
* @description 基于RESTFul风格的Shiro配置
-
*/
-
@Slf4j
-
@Configuration
-
public
class ShiroConfig {
-
-
@Bean
-
public DefaultWebSecurityManager securityManager(MyShiroRealm myShiroRealm) {
-
DefaultWebSecurityManager securityManager =
new DefaultWebSecurityManager();
-
securityManager.setRealm(myShiroRealm);
-
-
/**
-
* 关闭shiro自带的session管理
-
* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
-
*/
-
DefaultSubjectDAO subjectDAO =
new DefaultSubjectDAO();
-
DefaultSessionStorageEvaluator evaluator =
new DefaultSessionStorageEvaluator();
-
evaluator.setSessionStorageEnabled(
false);
-
subjectDAO.setSessionStorageEvaluator(evaluator);
-
securityManager.setSubjectDAO(subjectDAO);
-
-
return securityManager;
-
}
-
-
@Bean
-
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
-
ShiroFilterFactoryBean filterFactoryBean =
new ShiroFilterFactoryBean();
-
//filterFactoryBean.setLoginUrl("/sign-in");
-
//添加本身的过滤器并取名jwt
-
Map<String, Filter> filterMap =
new HashMap<>();
-
filterMap.put(
"jwt",
new JWTFilter());
-
filterFactoryBean.setFilters(filterMap);
-
filterFactoryBean.setSecurityManager(securityManager);
-
filterFactoryBean.setUnauthorizedUrl(
"/401");
-
-
//自定义url规则
-
Map<String, String> filterRuleMap =
new LinkedHashMap<>();
-
// 访问401和404页面不经过咱们的Filter
-
/*filterRuleMap.put("/401", "anon");
-
filterRuleMap.put("/403", "anon");
-
filterRuleMap.put("/404", "anon");
-
filterRuleMap.put("/sign-in", "anon");
-
filterRuleMap.put("/sign-up", "anon");
-
filterRuleMap.put("/sign-out", "logout");
-
// 全部请求经过本身的JWT Filter
-
filterRuleMap.put("/**", "jwt");*/
-
-
/*List<Permission> permissions = permissionMapper.selectAll();
-
for (Permission permission: permissions) {
-
filterRuleMap.put(permission.getUrl(), permission.getInit());
-
}
-
filterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);
-
log.info("Shiro Filter Factory Bean inject successful...");*/
-
return filterFactoryBean;
-
}
-
-
@Bean
-
@DependsOn(
"lifecycleBeanPostProcessor")
-
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
-
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
-
// 强制使用cglib,防止重复代理和可能引发代理出错的问题
-
// https://zhuanlan.zhihu.com/p/29161098
-
advisorAutoProxyCreator.setProxyTargetClass(
true);
-
return advisorAutoProxyCreator;
-
}
-
-
@Bean
-
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
-
return
new LifecycleBeanPostProcessor();
-
}
-
-
@Bean
-
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
-
AuthorizationAttributeSourceAdvisor advisor =
new AuthorizationAttributeSourceAdvisor();
-
advisor.setSecurityManager(securityManager);
-
return advisor;
-
}
-
-
}
这里配置的是无状态(stateless)的shiro配置,由于要用到RESTful协议,因此须要使用jwt来保证api的安全。因此这里须要关闭掉Shiro自带的Session管理器,而后启用Shiro注解,自定义URL规则会在稍后说到。安全
首先,来讲一下比较常见的基于角色的权限控制。session
咱们所说的角色,其实就是一系列权限的集合,里面指定了哪一种角色能够作什么事情,而用户也能够看做是一组角色的集合,因此咱们在使用第一种rbac来进行权限控制的时候,通常是判断一个用户是否有某一个角色。咱们用Shiro来说就是像下面这样:数据结构
-
@RequiresRoles(
"admin")
-
//或者
-
if(SecurityUtils.getSubject().hasRole(
"admin")) {
-
//do some thing...
-
System.out.println(
"I'm admin.")
-
}
上面的代码是不少文章集成shiro后成功的样子,我当时也是这么作的,可是随着后来权限管理的业务愈来愈复杂,这种简单的或者说静态的权限管理模型已经不适用了,举个栗子:若是如今我要增长一个角色,名字叫teacher,让它也能打印一点东西。那么要作的就是首先在数据库添加角色名,而后修改权限代码,改为:app
@RequiresRoles(value = {"admin", "teacher"}, logical = Logical.OR)
或者相似。也就是说,每一次变动需求,都须要变动代码,而后从新部署项目~~这显然是不怎么符合咱们预期要求的。less
所以这种模型只适合对权限需求变更不大的场景了。
固然,优势就是很是简单,开发起来使用起来很是方面,特别是配合上Shiro,一个注解就能搞定。
第二种基于资源的权限控制,是第一种的优化方案,咱们仍是来看看栗子:
-
@RequiresPermissions(
"user:add")
-
//或者
-
if(SecurityUtils.getSubject().hasPermission(
"user:add")) {
-
//do some thing...
-
System.out.println(
"user:add");
-
}
这种方式在必定程度上解决了上述第一种模型的问题,网上也有不少关于Resource Based Access Control的解释,可是这里我用我本身的话来解释一下,仍是用上面的栗子,客户要求“admin”和“teacher”都有权限去打印一些东西。因而,第二种就不须要再修改代码了,我直接在前台给teacher受权“user:add”这个权限就能够了啊,由于这样已经很明确的指定了执行这个方法须要什么权限,就是“user:add”权限,我如今无论你是什么角色,什么组织,什么模块也好,只要你拥有这个“user:add”这个权限,那你就能执行这个方法。
可是,问题又来了。
若是如今需求变动,执行这个方法须要另一个权限“user:edit”怎么办?是否是又得像上面那样,修改代码,从新部署项目。
因此,咱们还须要一种可以彻底动态控制权限的模型,不把任何权限或者角色写死,直接在数据库(前台)配置,每增长一个功能就注册一个功能而后指定它的须要的权限,就算需求变动,也不须要从新部署项目,只须要在前台稍微修改配置一下就能达到目的。
太晚了,今天就写到这里吧,预知后事如何,请听下回.......分解........
原文地址:https://blog.csdn.net/qq_33698579/article/details/80159823