RBAC0模型
最简单的用户、角色、权限模型。这里面又包含了2种:
用户和角色是多对一关系,即:一个用户只充当一种角色,一种角色可以有多个用户担当。
用户和角色是多对多关系,即:一个用户可同时充当多种角色,一种角色可以有多个用户担当。
那么,什么时候该使用多对一的权限体系,什么时候又该使用多对多的权限体系呢?
如果系统功能比较单一,使用人员较少,岗位权限相对清晰且确保不会出现兼岗的情况,此时可以考虑用多对一的权限体系。
其余情况尽量使用多对多的权限体系,保证系统的可扩展性。
如:张三既是行政,也负责财务工作,那张三就同时拥有行政和财务两个角色的权限。
项目环境
语言:kotlin(提供java版代码)
构建:gradle
框架:springboot + dubbo + jpa
按业务做的是1用户只能拥有1角色
FilterSecurityInterceptor
这个filter有几个要素,如下:
- SecurityMetadataSource (动态获取url)
- AccessDecisionManager (权限判断)
- AuthenticationManager (jwt配置)
动态获取url权限配置
kotlin代码:
@Component
class CustomMetadataSource : FilterInvocationSecurityMetadataSource {
@Reference // 用的dubbo,所以这里用这个注入
var systemResourceService: SystemResourceService? = null
private val antPathMatcher = AntPathMatcher()
override fun getAttributes(o: Any): Collection<ConfigAttribute> {
val fi = o as FilterInvocation
// 请求地址
val requestUrl = fi.requestUrl
// 获取系统权限资源配置 (这里就是去动态获取URL)
val resourceAndRole = systemResourceService!!.getResourceAndRole()
var needRoleList: MutableList<String> = ArrayList()
for (resourceRoleMap in resourceAndRole) {
// URL需要的角色,可能会有多个
if (antPathMatcher.match(resourceRoleMap["url"], requestUrl)) {
needRoleList.add(resourceRoleMap["role"].toString())
}
}
if (needRoleList.size > 0) {
val size = needRoleList.size
val values = arrayOfNulls<String>(size)
for (i in 0 until size) {
values[i] = needRoleList[i]
}
return SecurityConfig.createList(*values)
}
// 没有匹配上的资源,都是登录访问
return SecurityConfig.createList("ROLE_LOGIN")
}
override fun getAllConfigAttributes(): Collection<ConfigAttribute>? {
return null
}
override fun supports(clazz: Class<*>): Boolean {
return FilterInvocation::class.java.isAssignableFrom(clazz)
}
java代码:
@Component
public class CustomMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
MenuService menuService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) {
String requestUrl = ((FilterInvocation) o).getRequestUrl();
List<Menu> allMenu = menuService.getAllMenu();
for (Menu menu : allMenu) {
if (antPathMatcher.match(menu.getUrl(), requestUrl) && menu.getRoles().size() > 0) {
List<Role> roles = menu.getRoles();
int size = roles.size();
String[] values = new String[size];
for (int i = 0; i < size; i++) {
values[i] = roles.get(i).getName();
}
return SecurityConfig.createList(values);
}
}
//没有匹配上的资源,都是登录访问
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
权限资源URL所需角色判断
kotlin代码:
@Component
class UrlAccessDecisionManager : AccessDecisionManager {
private val log = LoggerFactory.getLogger(this.javaClass)
override fun decide(authentication: Authentication, o: Any, cas: Collection<ConfigAttribute>) {
val ite = cas.iterator()
while (ite.hasNext()) {
val ca = ite.next()
// 当前请求需要的权限
val needRole = ca.attribute
// log.info("访问" + o.toString() + "需要角色:" + needRole)
if ("ROLE_LOGIN" == needRole) {
if (authentication is AnonymousAuthenticationToken) {
throw BadCredentialsException("未登录")
} else
throw AccessDeniedException("权限不足")
}
// 遍历判断该url所需的角色看用户是否具备
for (ga in authentication.authorities) {
if (ga.authority == needRole) {
// 匹配到有对应角色,则允许通过
return
}
}
}
throw AccessDeniedException("权限不足")
}
override fun supports(attribute: ConfigAttribute?): Boolean {
return true
}
override fun supports(clazz: Class<*>?): Boolean {
return true
}
java代码:
@Component
public class UrlAccessDecisionManager