为了账号安全,请及时绑定邮箱和手机立即绑定

自定义RBAC(5)

标签:
Java

您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~


把实体类及Service类都准备好了之后就可以开始继续写业务代码了Spring Security的强大之一就在于它的拦截器那么这里也可以参照它的方式实现自己的拦截器

/**
 * 权限注解
 *
 * @author 湘王
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuthorize {
    // 组
    String group() default "";

    // 角色
    String role() default "";

    // 权限
    String permission() default "";
}

 

拦截器需要完成两个任务

1、查找用户拥有的资源其实分解一下也就是要完成下面的工作

1.1、找到某个用户所属的所有组(不需要去找这些组的父组)

1.2、找到某个用户拥有的所有角色(同时要逐个找到所有这些角色的父角色)

1.3、找到某个用户拥有的所有权限

2、将权限与资源做比对确认是否对该资源有访问权限

 

开始定义拦截处理器

/**
 * 拦截处理器
 *
 * @author 湘王
 */
@Aspect
@Component
public class InterceptorHandler {
   @Autowired
   private UserService userService;
   @Autowired
   private RoleService roleService;
   @Autowired
   private PermissionService permissionService;

   /**
    * 拦截controller包下面的所有类中,有@RequestMapping注解的方法
    */
   @Pointcut("execution(* com.xiangwang.controller..*.*(..)) " +
         "&& @annotation(org.springframework.web.bind.annotation.RequestMapping)")
   public void controllerMethodPointcut() {
   }

   /**
    * 拦截器具体实现
    */
   @Around("controllerMethodPointcut()")
   public Object Interceptor(final ProceedingJoinPoint pjp) {
      ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      HttpServletRequest request = sra.getRequest();
      Map<String, String> argsMap = new HashMap<String, String>();
      Enumeration<String> em = request.getParameterNames();
      String methodName = pjp.getSignature().getName();

      // 加入参数
      while (em.hasMoreElements()) {
         String paramName = em.nextElement();
         String value = request.getParameter(paramName);
         argsMap.put(paramName, value);
      }
      String username = argsMap.get("username");
      String platform = argsMap.get("platform");
      String timestamp = argsMap.get("timestamp");
      String signature = argsMap.get("signature");
      // 验证参数
      if (null == platform || null == timestamp || null == signature) {
         return "params required";
      }

      // 后端生成签名:platform = "xiangwang", timestamp = "159123456789", signature = a8354bc1b54a39528e81c549ec373c14
      String sign = DigestUtils.md5DigestAsHex((platform + timestamp).getBytes());
      if (!signature.equalsIgnoreCase(sign)) {
         return "signature error";
      }

      // 获取切面标记
      Signature signatureObject = pjp.getSignature();
      if (!(signatureObject instanceof MethodSignature)) {
         throw new IllegalArgumentException("this annotation can be applied on method only");
      }

      // 获取用户信息
      SysUser user = userService.queryByUsername(username);
      if (null == user) {
         return "user is not exist";
      }

      /**
       * 获得用户拥有的全部角色
       */
      // 用户所属的角色
      Set<SysRole> userRoleSet = new HashSet<>();
      // 用户所拥有的全部角色
      Set<SysRole> userAllRoleSet = new HashSet<>();
      // 查询这些角色的全部父角色
      StringBuilder roleIds = new StringBuilder();
      Set<String> userRoleNameSet = new HashSet<>();
      // 用户-组-角色
      List<SysRole> ugr = roleService.queryUGRByUserId(user.getId());
      if (null != ugr && ugr.size() > 0) {
         ugr.stream().forEach(r -> userRoleSet.add(r));
      }
      // 用户-角色
      List<SysRole> ur = roleService.queryURByUserId(user.getId());
      if (null != ur && ur.size() > 0) {
         ur.stream().forEach(r -> userRoleSet.add(r));
      }
      // 合并全部角色
      for (SysRole role : userRoleSet) {
         List<SysRole> list = roleService.queryParentsById(role.getParentids());
         if (null != list && list.size() > 0) {
            list.stream().forEach(r -> {
               userAllRoleSet.add(r);
               userAllRoleSet.add(role);
            });
         }
      }
      // 查询这些角色的全部权限
      userAllRoleSet.stream().forEach(r -> {
         roleIds.append(r.getId() + "," + r.getParentids());
         userRoleNameSet.add(r.getName());
      });
      List<SysPermission> rolePermissions = permissionService.queryByMultiRoleIds(roleIds.toString());

      /**
       * 获得用户拥有的全部权限
       */
      Set<String> userPermissionSet = new HashSet<>();
      // 用户-组-角色-权限
      List<SysPermission> ugrp = permissionService.queryUGRPByUserId(user.getId());
      if (null != ugrp && ugrp.size() > 0) {
         ugrp.stream().forEach(r -> userPermissionSet.add(r.getPath()));
      }
      // 用户-角色-权限
      List<SysPermission> urp = permissionService.queryURPByUserId(user.getId());
      if (null != urp && urp.size() > 0) {
         urp.stream().forEach(r -> userPermissionSet.add(r.getPath()));
      }
      // 用户-权限
      List<SysPermission> up = permissionService.queryUPByUserId(user.getId());
      if (null != up && up.size() > 0) {
         up.stream().forEach(r -> userPermissionSet.add(r.getPath()));
      }
      // 合并之前的结果
      if (null != rolePermissions && rolePermissions.size() > 0) {
         rolePermissions.stream().forEach(r -> userPermissionSet.add(r.getPath()));
      }

      // 权限判断
      Object target = pjp.getTarget();
      Class<?> clazz = target.getClass();
      MethodSignature methodSignature = (MethodSignature) signatureObject;
      try {
         Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
         // 获取注解类
         PreAuthorize preAuthorize = method.getDeclaredAnnotation(PreAuthorize.class);
         RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
         if (null != preAuthorize) {
            // 只有当用户具备此角色且访问路径与权限中的path相等时才能认为具有该资源的操作权限
            if (userRoleNameSet.contains(preAuthorize.role()) && userPermissionSet.contains(requestMapping.value()[0])) {
               System.out.println(username + " ==> " + clazz.getSimpleName() + " - " + method.getName() + " - " +
                     preAuthorize.role() + " - " + requestMapping.value()[0]);
               // 继续往下执行
               return pjp.proceed();
            } else {
               return "permission denied";
            }
         }
      } catch (NoSuchMethodException e) {
         e.printStackTrace();
      } catch (Throwable throwable) {
         throwable.printStackTrace();
      }

      return "faliure";
   }
}

 

最后来定义controller

/**
 * 用户Controller
 *
 * @author 湘王
 */
@RestController
public class UserController {
    /**
     * 接口访问:
     * 用例一:小林(uid=7)->组(无)->角色(产品,rid=5),结果:无权限
     * 用例二:小黄(uid=9)->组(无)->角色(客服,rid=4),结果:正常访问
     * 用例三:张总(uid=3)->组(gid=10001)->角色(客服、产品、运营,rid=4,5,6),结果:正常访问
     *
     */
    @PreAuthorize(role = "客服")
    @RequestMapping(value = "/api/v1.0.0/user/details", method = RequestMethod.GET)
    public String details(String username) {
        return username + " 有查看用户详情的权限";
    }

    /**
     * 接口访问:
     * 用例四:蔡总(uid=4)->组(gid=10002)->角色(会计、出纳、库管、配送,rid=7,8,9,10),结果:无权限
     *
     */
    @PreAuthorize(role = "产品")
    @RequestMapping(value = "/api/v1.0.0/system/setting/password", method = RequestMethod.GET)
    public String password(String username) {
        return username + " 有修改密码的权限";
    }
}

 

可以通过SQL来完成测试,看看被测试用户权限的正确性

用例一:小林(uid=7)->组(无)->角色(产品,rid=5),结果:无权限

用例二:小黄(uid=9)->组(无)->角色(客服,rid=4),结果:正常访问

用例三:张总(uid=3)->组(gid=10001)->角色(客服、产品、运营,rid=4,5,6),结果:正常访问

用例四:蔡总(uid=4)->组(gid=10002)->角色(会计、出纳、库管、配送,rid=7,8,9,10),结果:无权限

 

查找用户资源需要注意

1、查找权限,需要找到某个权限的所有子权限

2、查找角色,需要找到某个角色的所有父角色

4、查找组,可能需要找到父组,也可能不需要

5、想想这都是为什么?

5.1、因为权限是集合关系,角色是组合关系,组是既不是集合关系也不是聚合关系

5.2、父权限是某一类权限的代表,通过它可以找到相关的子权限

5.3、子角色是某个或某些角色的聚合/组合,这些角色组成了新角色

5.4、相对于父组,子组其实更多的是一种权限上的隔离

5.5、在设计权限系统时,需要了解这些隐含的语义逻辑和语境

 

最后再来看看遗留的一个小尾巴

之前在定义SysUser时有一个scope字段(0:全部,1:部门及以下,2:仅个人),它用来限制用户浏览数据的范围例如,在用户通过拦截器对权限的检查以后,当需要浏览诸如会员、商品、订单时,可以依据这个条件来过滤

具体做法就是在商品表中加入branchid和userid,然后依据scope来过滤

1、scope=1时,过滤条件增加branchid及以下机构(parentids LIKE '%?,%')

2、scope=2时,过滤条件增加userid(WHERE userid=?)

 

RBAC2和RBAC1的不同在于增加了一个sys_config表,用来实现权限相关的策略这个表主要在为用户分配组、角色、权限时起作用,而不是在拦截器中验证时例如,服务端有一个SystemController,其中有create/assign/update/remove + Group/Role/Permission之类的方法,执行这些方法时,就会从sys_config表中读取策略

 

例如,如果某两个角色A和B互斥,那么当给用户赋了A时,就不能再赋予用户B角色了

 

而所谓二级权限,就是用户A可以把自己拥有的权限再赋予另一个用户,即权限的转授实现二级权限也不复杂,只需要在SysPermission中增加对一个“分配权限”就行了如果用户A有这个“分配权限”的权限,就能把自己所拥有的权限分配给另外一个用户(但仅仅限于用户A所拥有的权限)依次类推,可以有三级、四级等多级分级授权

 

总的来说权限系统所用的技术并不复杂,也可以说非常简单但是设计权限系统的过程是比较费时费力的,也可能比较烧脑需要对业务需求有很深入的了解这样才能设计出既能满足需要,又简单实用的权限架构

其实大多数权限系统都只到角色这一层,真正用到组的并不多而且也没有二级权限、权限策略及用户数据范围

如果项目涉及到了上面这几样,恭喜你你已经把权限给看光了

 


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~



点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消