基于自定义注解实现简单的数据权限控制
基于自定义注解实现简单的数据权限控制
需求: 最近做一个物联网项目,需要使用权限,使每个角色只能操作自己的机器.
前言
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如有好的方案或者建议,欢迎指点或讨论

浙公网安备 33010602011771号