专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
Java编程精选  ·  手把手教你Java文件断点下载 ·  2 天前  
芋道源码  ·  李飞飞团队50美元训练出DeepSeek R1? ·  3 天前  
Java编程精选  ·  前阿里员工:内推了个38岁的研发,简历到HR ... ·  4 天前  
芋道源码  ·  SpringBoot3.4.0 结构化日志详解 ·  4 天前  
芋道源码  ·  裁员天花板:全员降薪40%,“闲置员工” ... ·  4 天前  
51好读  ›  专栏  ›  芋道源码

老板爱瞎改权限怎么办:注解+AOP 打造复杂权限体系

芋道源码  · 公众号  · Java  · 2025-02-09 17:19

正文

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入 芋道快速开发平台 知识星球。 下面是星球提供的部分资料:

👉 这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、 商城 、支付、工作流、大屏报表、微信公众号、 ERP CRM AI 大模型 等等功能:

  • Boot 多模块架构:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服务架构:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 双版本

来源:juejin.cn/post/
7425441107101417508


引言

在软件开发的世界里,权限控制是确保数据安全和业务流程合规性的关键。然而,当老板或管理层频繁地调整权限设置时,这不仅会打乱开发节奏,还可能导致安全漏洞和性能问题。这种情况下,一个灵活、可扩展且易于管理的权限控制系统就显得尤为重要。

本文将探讨如何使用Java注解和Spring AOP(面向切面编程)来构建一个复杂而强大的权限控制体系。这个体系将能够应对频繁变更的权限需求,同时确保系统的安全性和稳定性。

在这个体系中,我们将通过注解来声明权限需求,利用AOP来实现权限检查的逻辑。这种方法不仅可以减少代码侵入性,还能提高系统的可维护性和可扩展性。我们将从权限控制的基本概念出发,逐步深入到系统需求分析、数据库设计、注解定义、切面实现,以及业务逻辑的实现。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

1. 权限控制的基本概念

权限控制是软件系统安全性的核心组成部分,它确保了只有授权的用户才能访问特定的资源或执行特定的操作。在企业级应用中,权限控制的复杂性随着业务需求的增加而增加,这要求系统能够灵活地处理不同层次和类型的权限。

定义组织权限和个人权限

组织权限通常指的是基于用户所属组织结构的权限。这种权限控制方式允许系统根据用户所在的部门、团队或公司层级来限制或授权访问。例如,一个公司的财务部门可能只能访问与财务相关的数据和功能,而不能访问人力资源或研发部门的数据。

个人权限则是指分配给特定用户的权限,与用户所属的组织结构无关。这种权限控制方式允许系统为每个用户定制访问控制,以满足个性化的需求。例如,一个销售代表可能被授权访问客户数据,但仅限于他们自己的客户记录。

权限的层次结构和作用域

权限控制系统通常采用层次结构来组织权限,以便于管理和维护。在这种结构中,权限可以被分为不同的级别,每个级别对应不同的访问控制需求。例如,一个简单的权限层次结构可能包括“只读”、“编辑”和“管理”三个级别。

权限的作用域决定了权限的适用范围。在某些系统中,权限可能仅适用于特定的资源或操作,而在其他系统中,权限可能跨越多个资源或操作。例如,一个用户可能被授权访问特定的数据库表,但仅限于查询操作,而不能进行数据的插入或删除。

权限与角色的关系

在权限控制系统中,角色是将权限分配给用户的一个有效方式。通过为不同的角色分配不同的权限集合,系统可以简化权限管理,并提高灵活性。例如,一个“管理员”角色可能拥有访问和修改所有资源的权限,而一个“普通用户”角色可能只有访问特定资源的权限。

角色和权限之间的关系通常是多对多的。一个用户可以拥有多个角色,而一个角色也可以包含多个权限。这种关系允许系统灵活地为用户分配权限,同时也方便了权限的更新和维护。

在复杂的系统中,角色和权限的关系可能更加复杂。例如,可能存在继承关系,其中某些角色继承了其他角色的权限,或者存在特定的规则来限制角色的权限组合。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

2. 系统需求分析

在设计一个复杂的权限控制系统时,需求分析是至关重要的第一步。它确保我们能够准确理解业务需求,并设计出一个既能满足当前需求又能适应未来变化的系统。以下是对复杂权限控制需求的描述,以及确定的业务规则。

描述复杂的权限控制需求

组织权限

组织权限要求系统能够根据用户所属的组织结构来授权或限制对特定功能的访问。例如,一个组织可能有不同的部门,每个部门可能有不同的权限集。

个人权限

个人权限要求系统能够为每个用户设置特定的权限,这些权限可能与用户的角色或组织结构无关。例如,某些用户可能被赋予访问敏感数据的权限。

数量限制

数量限制要求系统能够跟踪和限制用户或组织可以执行的特定操作的数量。例如,一个用户可能在一个时间段内只能提交一定数量的请求。

特殊角色权限

特殊角色权限要求系统能够识别并授权某些特殊角色,这些角色可能拥有超越常规权限控制的权限。例如,系统管理员可能可以访问系统的所有部分,不受常规权限限制。

确定权限控制的业务规则

  • 权限继承: 用户继承其所属组织和角色的权限。
  • 权限覆盖: 个人权限可以覆盖组织权限。
  • 数量限制: 用户的权限可能受到数量限制,一旦达到限制,即使有权限也无法执行操作。
  • 角色优先级: 特殊角色可能具有高优先级,可以覆盖其他所有权限设置。
  • 权限审计: 所有权限的授予和使用都应该被记录和审计,以确保系统的安全性和合规性。

流程图

下面是一个简化的流程图,展示了如何通过注解和切面来实现上述复杂的权限控制需求:

在这个架构中,用户请求首先到达 Spring Security Filter ,然后进入权限检查环节。权限检查包括特殊角色权限检查、数量限制检查、组织权限检查和个人权限检查。如果用户通过所有检查,则可以访问业务逻辑处理;如果任何一个检查失败,请求将被拒绝。

3. 库表设计

在设计一个复杂的权限控制系统时,数据库的设计是核心部分,它需要能够支持组织权限、个人权限、数量限制以及特殊角色权限等功能。以下是针对这些需求设计的数据库表结构。

组织表( Organizations

字段名 数据类型 描述
organization_id INT 主键,自增
name VARCHAR 组织名称
parent_id INT 父组织ID,用于层级关系
description TEXT 组织描述

用户表( Users

字段名 数据类型 描述
user_id INT 主键,自增
username VARCHAR 用户名
password_hash VARCHAR 加密密码
organization_id INT 所属组织ID
email VARCHAR 电子邮件
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

角色表( Roles

字段名 数据类型 描述
role_id INT 主键,自增
name VARCHAR 角色名称
description TEXT 角色描述

权限表( Permissions

字段名 数据类型 描述
permission_id INT 主键,自增
name VARCHAR 权限名称
description TEXT 权限描述

角色权限关联表( Role_Permissions

字段名 数据类型 描述
role_id INT 角色ID,外键
permission_id INT 权限ID,外键

用户角色关联表( User_Roles

字段名 数据类型 描述
user_id INT 用户ID,外键
role_id INT 角色ID,外键

组织权限关联表( Organization_Permissions

字段名 数据类型 描述
organization_id INT 组织ID,外键
permission_id INT 权限ID,外键
limit INT 允许的操作数量限制

数据库设计说明

  • 组织表: 存储组织的基本信息,包括层级关系。
  • 用户表: 存储用户的基本信息,包括所属组织的ID。
  • 角色表: 存储角色的基本信息。
  • 权限表: 存储权限的基本信息。
  • 角色权限关联表: 定义角色和权限之间的关系,一个角色可以有多个权限,一个权限可以被多个角色拥有。
  • 用户角色关联表: 定义用户和角色之间的关系,一个用户可以有多个角色。
  • 组织权限关联表: 定义组织和权限之间的关系,并设置操作数量限制。

这种设计允许系统灵活地管理复杂的权限控制需求,包括组织权限、个人权限、数量限制和特殊角色权限。通过外键关联,可以确保数据的一致性和完整性。在实际应用中,可能还需要根据具体需求添加更多的字段或表,例如,用于跟踪权限使用情况的审计日志表。

请注意,这只是一个基本的设计,实际的数据库设计可能需要根据具体的业务需求和性能要求进行调整。在实现时,还需要考虑索引优化、数据一致性、备份和恢复策略等因素。

4. 权限控制的注解设计

在Java中,注解(Annotation)是一种特殊的接口,用于为代码提供元数据。在权限控制中,注解可以被用来标记需要进行权限检查的方法或类。以下是为权限控制设计的三个注解: @PermissionRequired @RoleRequired @LimitRequired

定义权限注解(@PermissionRequired)

@PermissionRequired 注解用于标记需要特定权限才能访问的方法。它接受一个或多个权限名称作为参数,表示用户必须拥有这些权限之一才能执行该方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionRequired {
    String[] value();
}
定义角色注解(@RoleRequired)

@RoleRequired 注解用于标记需要特定角色才能访问的方法。它接受一个或多个角色名称作为参数,表示用户必须拥有这些角色之一才能执行该方法。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RoleRequired {
    String[] value();
}
定义数量限制注解(@LimitRequired)

@LimitRequired 注解用于标记需要检查数量限制的方法。它接受一个参数,表示该方法对应的操作类型,这个操作类型将用于检查用户或组织是否达到了操作数量的限制。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LimitRequired {
    String value();
}
使用示例

以下是如何使用这些注解的示例:

@RestController
@RequestMapping("/api")
public class ApiController {

    @PermissionRequired("READ_DATA")
    @GetMapping("/data")
    public ResponseEntity> getData() {
        // 方法逻辑
    }

    @RoleRequired({"ADMIN""MANAGER"})
    @PostMapping("/config")
    public ResponseEntity> updateConfig() {
        // 方法逻辑
    }

    @LimitRequired("REQUEST_LIMIT")
    @GetMapping("/request")
    public ResponseEntity> makeRequest() {
        // 方法逻辑
    }
}

在这个示例中,getData 方法需要用户拥有 READ_DATA 权限, updateConfig 方法需要用户拥有 ADMIN MANAGER 角色,而 makeRequest 方法需要检查 REQUEST_LIMIT 类型的数量限制。

权限注解的实现细节

这些注解的实现将依赖于Spring框架的AOP(面向切面编程)功能。通过创建相应的切面,我们可以在方法执行前拦截这些注解,并执行权限检查逻辑。如果用户没有通过检查,系统将阻止方法的执行并返回相应的错误信息。

5. 切面实现

在Spring框架中,AOP(面向切面编程)是一种编程范式,它允许我们在不修改业务逻辑代码的情况下,对横切关注点(如日志记录、事务管理、权限检查等)进行模块化。通过使用AOP,我们可以将权限检查逻辑从业务逻辑中分离出来,提高代码的可维护性和重用性。

介绍Spring AOP的基本概念

Spring AOP基于代理机制,它允许我们在不改变现有代码结构的情况下,通过声明额外的逻辑来扩展程序的行为。在Spring AOP中,主要涉及以下几个核心概念:

  • 切面(Aspect): 切面是封装横切关注点的类,比如日志、事务、权限控制等。
  • 连接点(Join point): 连接点是程序执行过程中的一个点,如方法的调用或异常的抛出。在Spring AOP中,连接点总是方法的执行。
  • 通知(Advice): 通知是切面在某连接点上的执行动作,它定义了在何时何地执行哪些逻辑。常见的通知类型包括:前置通知( Before )、后置通知( After )、返回通知( AfterReturning )、异常通知( AfterThrowing )和环绕通知( Around )。
  • 切点(Pointcut): 切点是一组匹配连接点的规则,它定义了哪些连接点会被切面所拦截。
  • 目标对象(Target Object): 目标对象是指被代理的对象,即包含连接点的对象。
  • 代理(Proxy): 代理是AOP框架创建的,它包裹了目标对象,并在执行连接点之前或之后执行通知。
定义权限检查切面(PermissionAspect)

PermissionAspect 是一个切面类,它使用Spring AOP来拦截被权限注解标记的方法,并执行权限检查逻辑。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PermissionAspect {

    @Pointcut("@annotation(permissionRequired)")
    public void permissionPointcut(PermissionRequired permissionRequired) {}

    @Before("permissionPointcut(permissionRequired)")
    public void checkPermission(JoinPoint joinPoint, PermissionRequired permissionRequired) {
        // 获取当前用户
        User currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        // 检查用户是否拥有所需的权限
        for (String permission : permissionRequired.value()) {
            if (!permissionService.hasPermission(currentUser, permission)) {
                throw new AccessDeniedException("Access Denied: No permission to perform this operation.");
            }
        }
    }

    // 其他方法,如角色检查、数量限制检查等
}
组织权限检查

组织权限检查需要查询数据库,确定当前用户所属的组织是否拥有特定的权限。

public boolean hasOrganizationPermission(User currentUser, String permission) {
    // 查询数据库,检查用户所属组织是否有权限
}
个人权限检查

个人权限检查需要查询数据库,确定当前用户是否拥有特定的权限。

public boolean hasUserPermission(User currentUser, String permission) {
    // 查询数据库,检查用户是否有权限
}
数量限制检查

数量限制检查需要查询数据库,确定当前用户或组织是否达到了操作数量的限制。

public boolean isLimitExceeded(User currentUser, String limitType) {
    // 查询数据库,检查是否达到数量限制
}
特殊角色检查

特殊角色检查需要查询数据库,确定当前用户是否拥有特殊角色。

public boolean hasSpecialRole(User currentUser) {
    // 查询数据库,检查用户是否拥有特殊角色
}
综合检查逻辑

在实际的权限检查逻辑中,我们需要综合考虑组织权限、个人权限、数量限制和特殊角色。这可能涉及到多个方法的调用和复杂的逻辑判断。

PermissionAspect 中,我们可以使用环绕通知( Around Advice )来实现这一逻辑,确保在方法执行前后进行必要的检查。

6. 权限控制的业务逻辑实现

在权限控制系统中,业务逻辑层是实现权限检查功能的核心。它负责与数据库交互,获取用户、角色和权限数据,并根据这些数据执行权限检查。以下是三个关键服务的实现: UserService RoleService PermissionService







请到「今天看啥」查看全文