写权限遇到的坑

写权限遇到的坑

项目中希望增加一层权限管理,分为多个层级,自顶向下为一、二、三...

顶级的权限往往是最高的,上下层之间的权限是无关的,也就是面上写层级,实际上是一个flat的权限列表

分层的意义在于,同一个接口(数据修改接口)各层级的不同权限都有可能允许访问

问题分析

多个层,每个层有多个角色(可以认为从0开始编号,编号从数据库或配置中心获取,不同层的权限标识获取渠道不一)

作用于方法(API接口)

一个数据修改接口,允许混合多层的角色,例如:

L1: true    // super user layer, if true then the user is admin
L2: [2]     // group user layer
L3: [1,3]   // project user layer 

对于这类复合权限,一个简单的思路是建立一个带逻辑连接的注解

实施

目录树

├─annotation
│      SuperUserLayerPermission.java
│      GroupUserLayerPermission.java
│      ProjectUserLayerPermission.java
│      UnionUserLayerPermission.java
│
├─aspect
│      PermissionAspect.java
│
├─common
│      UserThreadLocal.java
│
├─enums
│      GroupUserRole.java
│      LogicPermission.java
│      ProjectUserRole.java
│
├─service
│      SuperUserLayerPermissionService.java
│      GroupUserLayerPermissionService.java
│      ProjectUserLayerPermissionService.java
│      UnionUserLayerPermissionService.java
│
└─web

分层,对于每一层:

Enum

L1 --> 不需要建立,没有多个权限角色,只有 {true, false}

L2 --> G1 ~ G7

public enum GroupUserRole {

    G1(1, "组内权限1"),
    G2(2, "组内权限2"),
    G3(3, "组内权限3"),
    G4(4, "组内权限4"),
    G5(5, "组内权限5"),
    G6(6, "组内权限6"),
    G7(7, "组内权限7"),
    ;

    final int index;
    final String code;

    GroupUserRole(int index, String code) {
        this.index = index;
        this.code = code;
    }

    public int getIndex() {
        return index;
    }

    public String getCode() {
        return code;
    }
}

L3 --> P1 ~ P4

public enum ProjectUserRole {
    P1(1, "项目权限1"),
    P2(2, "项目权限2"),
    P3(3, "项目权限3"),
    P4(4, "项目权限4"),
    ;

    final int index;
    final String code;

    ProjectUserRole(int index, String code) {
        this.index = index;
        this.code = code;
    }

    public int getIndex() {
        return index;
    }

    public String getCode() {
        return code;
    }
}

逻辑连接枚举

最简单的逻辑连接:AND, OR

public enum LogicPermission {
    AND(1,"和"),
    OR(2, "或"),
    ;

    final int index;
    final String code;

    LogicPermission(int index, String code) {
        this.index = index;
        this.code = code;
    }

    public int getIndex() {
        return index;
    }

    public String getCode() {
        return code;
    }
}

Annotation

L1 --> super user layer 用于标志是否为超级用户

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SuperUserLayerPermission {}

L2 --> group user layer

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GroupUserLayerPermission {
    GroupUserRole[] roles() default {};
}

L3 --> project user layer

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ProjectUserLayerPermission {
    ProjectUserRole[] roles() default {};
}

组合

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UnionUserLayerPermission {
    LogicPermission relation() default LogicPermission.OR;
    SuperUserLayerPermission[] superUserLayerPermission() default {};
    GroupUserLayerPermission[] groupUserLayerPermission() default {};
    ProjectUserLayerPermission[] projectUserLayerPermission() default {};
}

Service

superUser

@Service
public class SuperUserLayerPermissionService {

    @Resource
    private Map<String, Boolean> superUserMap; // 模拟用户权限的配置

    public boolean checkSuperUser(SuperUserLayerPermission permission, String userId) {
        return checkSU(userId);
    }

    private boolean checkSU(String userId) {
        // 模拟判断用户权限的过程
        return BooleanUtils.isTrue(superUserMap.get(userId));
    }
}

groupUser

@Service
public class GroupUserLayerPermissionService {
    @Resource
    private Map<String, Integer> groupUserMap; // 模拟用户权限的配置

    public boolean checkGroupUser(GroupUserLayerPermission permission, String userId) {
        GroupUserRole[] roles;
        if (permission == null || (roles = permission.roles()) == null || roles.length == 0) {
            return false;
        }

        Integer roleIndex = checkGroup(userId);
        if (roleIndex == null) {
            return false;
        }
        return Arrays.stream(roles).anyMatch(r -> r.getIndex() == roleIndex);
    }

    private Integer checkGroup(String userId) {
        return groupUserMap.get(userId);
    }
}

projectUser

@Service
public class ProjectUserLayerPermissionService {
    @Resource
    private Map<String, Integer> projectUserMap; // 模拟用户权限的配置

    public boolean checkProjectUser(ProjectUserLayerPermission permission, String userId) {
        ProjectUserRole[] roles;
        if (permission == null || (roles = permission.roles()) == null || roles.length == 0) {
            return false;
        }

        Integer roleIndex = checkProject(userId);
        if (roleIndex == null) {
            return false;
        }
        return Arrays.stream(roles).anyMatch(r -> r.getIndex() == roleIndex);

    }

    private Integer checkProject(String userId) {
        return projectUserMap.get(userId);
    }
}

union

@Service
public class UnionUserLayerPermissionService {
    @Resource
    private SuperUserLayerPermissionService superUserLayerPermissionService;

    @Resource
    private GroupUserLayerPermissionService groupUserLayerPermissionService;

    @Resource
    private ProjectUserLayerPermissionService projectUserLayerPermissionService;

    public boolean checkUnion(UnionUserLayerPermission permission, String userId) {
        LogicPermission relation = permission.relation();
        if (relation == null) {
            return false;
        }

        Boolean superUserPermission = null;
        SuperUserLayerPermission[] superUserLayerPermissions = permission.superUserLayerPermission();
        if (permission.superUserLayerPermission().length > 0) {
            superUserPermission = superUserLayerPermissionService.checkSuperUser(superUserLayerPermissions[0], userId);
        }

        Boolean groupUserPermission = null;
        GroupUserLayerPermission[] groupUserLayerPermissions = permission.groupUserLayerPermission();
        if (groupUserLayerPermissions.length > 0) {
            groupUserPermission = groupUserLayerPermissionService.checkGroupUser(groupUserLayerPermissions[0], userId);
        }

        Boolean projectUserPermission = null;
        ProjectUserLayerPermission[] projectUserLayerPermissions = permission.projectUserLayerPermission();
        if (projectUserLayerPermissions.length > 0) {
            projectUserPermission = projectUserLayerPermissionService.checkProjectUser(projectUserLayerPermissions[0], userId);
        }

        Stream<Boolean> booleanStream = Stream.of(superUserPermission, groupUserPermission, projectUserPermission).filter(Objects::nonNull);
        if (relation == LogicPermission.AND) {
            return booleanStream.allMatch(bool -> bool); // 都为 true
        } else if (relation == LogicPermission.OR) {
            return booleanStream.anyMatch(bool -> bool); // 一个为 true
        } else {
            return false;
        }
    }
}

通用工具-获取userId

public class UserThreadLocal {

    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static String currentUserId() {
        return threadLocal.get();
    }

    public static void setUserId(String userId) {
        threadLocal.set(userId);
    }
}

Aspect

作用于目标包路径下的所有函数 * com.roles.web.*.*(..)

@Component
@Aspect
public class PermissionAspect {

    @Resource
    private SuperUserLayerPermissionService superUserLayerPermissionService;

    @Resource
    private GroupUserLayerPermissionService groupUserLayerPermissionService;

    @Resource
    private ProjectUserLayerPermissionService projectUserLayerPermissionService;

    @Resource
    private UnionUserLayerPermissionService unionUserLayerPermissionService;

    @Pointcut("execution(* com.roles.web.*.*(..))")
    public void permissionPointCut() {}

    @Pointcut("@annotation(permission)")
    public void annotationSuperUserLayerPermission(SuperUserLayerPermission permission) {}

    @Pointcut("@annotation(permission)")
    public void annotationGroupUserLayerPermission(GroupUserLayerPermission permission) {}

    @Pointcut("@annotation(permission)")
    public void annotationProjectUserLayerPermission(ProjectUserLayerPermission permission) {}

    @Pointcut("@annotation(permission)")
    public void annotationUnionUserLayerPermission(UnionUserLayerPermission permission) {}

    @Around(value = "permissionPointCut() && annotationSuperUserLayerPermission(permission)", argNames = "joinPoint,permission")
    public Object privilege(ProceedingJoinPoint joinPoint, SuperUserLayerPermission permission) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String userId = UserThreadLocal.currentUserId();
        if (superUserLayerPermissionService.checkSuperUser(permission, userId)) {
            return joinPoint.proceed();
        } else {
            // log
            String msg = String.format("%s has not permission at %s.%s", userId, className, methodName);
            System.out.println(msg);
            throw new RuntimeException(msg);
        }
    }

    @Around(value = "permissionPointCut() && annotationGroupUserLayerPermission(permission)", argNames = "joinPoint,permission")
    public Object privilege(ProceedingJoinPoint joinPoint, GroupUserLayerPermission permission) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String userId = UserThreadLocal.currentUserId();
        if (groupUserLayerPermissionService.checkGroupUser(permission, userId)) {
            return joinPoint.proceed();
        } else {
            // log
            String msg = String.format("%s has not permission at %s.%s", userId, className, methodName);
            System.out.println(msg);
            throw new RuntimeException(msg);
        }
    }

    @Around(value = "permissionPointCut() && annotationProjectUserLayerPermission(permission)", argNames = "joinPoint,permission")
    public Object privilege(ProceedingJoinPoint joinPoint, ProjectUserLayerPermission permission) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String userId = UserThreadLocal.currentUserId();
        if (projectUserLayerPermissionService.checkProjectUser(permission, userId)) {
            return joinPoint.proceed();
        } else {
            // log
            String msg = String.format("%s has not permission at %s.%s", userId, className, methodName);
            System.out.println(msg);
            throw new RuntimeException(msg);
        }
    }

    @Around(value = "permissionPointCut() && annotationUnionUserLayerPermission(permission)", argNames = "joinPoint,permission")
    public Object privilege(ProceedingJoinPoint joinPoint, UnionUserLayerPermission permission) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String userId = UserThreadLocal.currentUserId();
        if (unionUserLayerPermissionService.checkUnion(permission, userId)) {
            return joinPoint.proceed();
        } else {
            // log
            String msg = String.format("%s has not permission at %s.%s", userId, className, methodName);
            System.out.println(msg);
            throw new RuntimeException(msg);
        }
    }

}

使用案例

处于Aspect目标包路径下 * com.roles.web.*.*(..)

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/hello")
    @UnionUserLayerPermission(
            relation = LogicPermission.AND,
            superUserLayerPermission = @SuperUserLayerPermission,
            groupUserLayerPermission = @GroupUserLayerPermission(roles = {GroupUserRole.G2}),
            projectUserLayerPermission = @ProjectUserLayerPermission(roles = {ProjectUserRole.P1, ProjectUserRole.P3})
    )
    public boolean test() {
        return true;
    }

    @GetMapping("/world")
    @SuperUserLayerPermission
    public boolean hello() {
        return true;
    }
}
posted @ 2023-07-24 16:19  jentreywang  阅读(14)  评论(0编辑  收藏  举报