security+jwt 动态权限控制RBAC0

本文介绍了如何使用Spring Security和JWT实现基于RBAC0模型的动态权限控制。通过动态获取URL权限配置、权限资源URL所需角色判断以及JWT认证功能,实现了用户、角色、权限的灵活管理。在项目环境中,使用了Kotlin、Gradle、Spring Boot和Dubbo。文章还提供了Java和Kotlin的代码示例,包括FilterSecurityInterceptor、JWT登录处理和Token验证等关键部分。
摘要由CSDN通过智能技术生成

在这里插入图片描述

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 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值