前言
前面一讲中,主要是针对于简单角色,权限规则基本不变的情况。我们通过写代码将其基本权限控制写到代码中去。
在一个内管系统中,角色众多,权限复杂,权限规则随着公司和业务的发展不断变化。还有菜单控制给用户是否查看,能否访问。 这个时候,这样的场景我们是不能去写代码的。人员都会时常变得,我们需要把这些数据存储到数据库中。
内容
1.RBAC数据模型
表结构:牵涉到5张表,3张主表,2张关系表。
5张表中有4张是业务人员维护的,其中有一张表是开发人员维护的(资源表)
2.开发
新建模块spring-security-authorize工程,这个工程里面实现的rbac,提供基本的增删查改和controller接口。
我们需要知道的是spring-security-authorize跟spring-security没有关系的,他只是我们根据我们业务需求开发的数据表上的增删改查。跟Spring-security有关系的是啥?你的安全模块最终要控制的是这些url能不能访问?有关系的是这些url。url能不能访问。用户能不能访问此url。
用户最终的url是存储到我们的资源表的,那这些url如何跟我们的spring-security怎样关联起来呢?
我们要实现我们自己写的权限模块和spring-security对接起来。对接的话,我们需要做一些配置。我们需要在自己的模块做一些配置?也需要在Spring-Security模块做一些配置。
2.1 spring-security-authorize模块
我们在spring-security-authorize模块pom.xml文件中添加依赖:
public interface RbacService {
/**
* 判断是否能够访问
* @param request 用户请求
* @param authentication 用户信息
* @return
*/
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
创建实现类:
@Component("rbacService")
public class RbacServiceImpl implements RbacService {
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object principal = authentication.getPrincipal();
boolean hasPermission = false;
if (principal instanceof UserDetails){
//获取用户名
String username = ((UserDetails) principal).getUsername();
//拿到用户名之后,我们可以根据RBAC的数据结构,去查找用户名对应用户角色
//然后根据用户角色查询用户菜单和按钮,最终拿到所有的url
Set<String> urls = new HashSet<>();//简单起见,我们这里创建url,实际上需要根据数据库查询
for (String url:urls) {
//由于用户访问的请求可能有"/user/*" 也有"/user/1",这种请求,所以我们不能用equals比较,需要用AntPathMatcher
if(antPathMatcher.match(url,request.getRequestURI())){
hasPermission = true;
break;
}
}
}
return hasPermission;
}
2.2 spring-security-core模块
我们在spring-security里面需要做的是:我们需要写一个权限表达式去调用上面写的RbacService的hasPermission方法。最终spring-security看能不能过,最终都是一个权限表达式。
我们在spring-security里面配置:
由此我们产生了2个问题:
2.2.1 问题1
我们在上面spring-security-demo的Provider里面配置了anyRequest(),说明任何请求都需要走这个规则,但是我们在之前spring-security-core的Manager里面也已经写过了:
因为MyAuthorizeConfigManager是在最后调用的,所以MyAuthorizeConfigManager的配置会覆盖掉: DemoAuthorizeConfigProvider,我们我们先注释
2.2.1 问题2
因为我们的anyRequest()是这对于所有请求的。
那么下面的配置一定要在最后生效。
如果我们一上来就读取上面配置,那么所有请求都走上面配置的话就会有问题,因为我们安全模块里面登录授权部分请求是需要permitAll();
所以要求我们读取顺序是有要求的:
我们要求我们的安全模块先读取,然后再读取授权模块的request。
我们需要使用注解:@Order
因为DemoAuthorizeConfigProvider里面是最大值,所以是最后读取出来的。
以上加载Provider是有顺序的,所以我们的Manager里面是一个有序的集合:List
然后我们在spring-security-demo项目的pom.xml文件添加:spring-security-authorize依赖。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。