基于自定义注解实现简单的数据权限控制

基于自定义注解实现简单的数据权限控制

需求: 最近做一个物联网项目,需要使用权限,使每个角色只能操作自己的机器.


前言

1、目前系统拥有3个角色1️⃣:创建者2️⃣管理者3️⃣观察者
权限1⃣️包含2⃣️的所有权限,2⃣️包含3⃣️的所有权限

2、机器与用户为多对多的关系,即一个用户对应多个机器,一个机器对用多个用户.

3、权限表,因为权限简单没有做细分.所以数据库存在userId-roleId-deviceId的关系表.

4、自定义注解一般使用AOP或者过滤器开发,我们这里使用AOP.


开发

1、引入AOP所需包
   <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
2、创建注解

ps:因为所有的Controller都有继承BaseConller的getUserId()方法,所有可以取到userId

@Target(ElementType.METHOD)  //  注解用于字段上
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
public @interface VerifyPermissions {
    long permissions();//角色Id

    /**
     * deviceId参数的 名称  方便接口去获取device的值
     * 默认是deviceId
     * 如果参数在实体类里  使用  对象.属性
     * @return
     */
    String type() default "deviceId";//标记deviceId的属性名称  方便切换 默认deviceId
}
3、Aop验证逻辑
@Aspect//来定义一个切面
@Component
public class VerifyPermissionsAop {



    @Autowired
    private DeviceUserService deviceUserService;

    //定义切入点
    @Pointcut("@annotation(com.voltronic.iotInv.app.util.VerifyPermissions)")
    public void auditAspect() {


    }

    //通知
    @Before("auditAspect()&& @annotation(verifyPermissions)")
    public void doBefore(JoinPoint joinPoint,VerifyPermissions verifyPermissions) {
        getControllerMethodDescription(joinPoint,verifyPermissions);
    }


    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @param verifyPermissions注解
     * @return 方法描述
     */
    public void getControllerMethodDescription(JoinPoint joinPoint,VerifyPermissions verifyPermissions) {
        String targetName = joinPoint.getTarget().getClass().getName();    //获得执行方法的类名
        try {
            Class targetClass = Class.forName(targetName);//获得方法类
            Object bean = SpringUtil.getBean(targetClass);//从Spring上下文获取controller对象
            Field[] fields = targetClass.getFields();
            //getUserId是定义在Controller的方法 用于获取当前用户Id
            Method method = targetClass.getMethod("getUserId");    //获取getUserId方法
            //获取切入点(方法)的所有参数值
            Object[] args = joinPoint.getArgs();
           //获取切入点(方法)的所有参数名称(这两个数组一一对应)
            String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); // 参数名
            String type = verifyPermissions.type();//获取注解的type值
            //在这里的作用是更加方便的取到设备的Id,告诉注解哪个属性是设备的Id
            Long deviceId = null;
           //如果设备id在对象里可以使用    对象.属性名  不支持多层
            boolean contains = type.contains(".");
            if (contains){
                String[] split = type.split(".");
                if (split.length!=2|| StringUtils.isEmpty(split[0])|| StringUtils.isEmpty(split[1])){
                    throw new BusinessException(Constants.BUSI_OTHER_ERROR, "自定义注解使用错误");
                }
                //便利所有的参数 获取到需要的设备id
                for (int i = 0; i < argNames.length; i++) {
                    if(argNames[i].endsWith(split[0])){
                        Object entityVO = args[i];
                        System.out.println("进入实体类:请在实体类里寻找参数!");
                        Class<?> aClass = entityVO.getClass();
                        Field[] declaredFields = aClass.getDeclaredFields();
                        Field deviceId1 = aClass.getDeclaredField("deviceId");
                        deviceId = (Long) deviceId1.get("deviceId");
                    }
                }
            }else{
                //在最外层
                for (int i = 0; i < argNames.length; i++) {
                    if(argNames[i].equals(type)){
                        deviceId = (Long)args[i];
                    }
                }
            }
             //执行前面的getUserId方法    得到用户Id
            Long userId = (Long)method.invoke(bean);
            //开始判断权限
            //获取用户的权限
            Long permission = deviceUserService.findPermissionByUserIdAndDeviceId(userId, deviceId);
            //为空等于无权限
            Assert.notNull(permission,"权限不足");
            long permissions = verifyPermissions.permissions();

            //1创建者  2管理者 3查看者  小的权限大
            if (permission.longValue()>permissions){
                throw new BusinessException(Constants.BUSI_OTHER_ERROR, "权限不足");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4、使用注解
@PostMapping(value = "/test")
@ApiOperation(value="测试",notes="测试")
@VerifyPermissions(permissions = 0L,type = "deviceId")
public Object test(Long userId, Long deviceId, UserVO userVO){
    System.out.println("开始追寻");
    return RespEntity.successResp("successful");
}

emmmm如有好的方案或者建议,欢迎指点或讨论

posted @ 2021-11-18 14:12  暮雪ee  阅读(469)  评论(0)    收藏  举报