第二十三节 @SaCheckPermission注解的解析

亮子 | 2026-04-08 10:50:08 | 25 | 0 | 0 | 0

我来详细讲解一下 @SaCheckPermission 注解。

@SaCheckPermission 注解详解

一、基本概念

@SaCheckPermissionSa-Token 权限认证框架提供的一个**权限校验注解**,用于对接口方法进行权限控制。它属于 Sa-Token 框架的核心功能之一。

在你的项目中,这个注解来自:

import cn.dev33.satoken.annotation.SaCheckPermission;

二、核心作用

该注解用于**声明式权限校验**,在方法执行前自动检查当前登录用户是否具有指定的权限标识。如果没有权限,会抛出异常并返回相应的错误信息。


三、使用方式

1. 基本用法

@SaCheckPermission("system:user:add")
@PostMapping("/add")
public R<Void> add(@RequestBody SysUserBo user) {
    return toAjax(sysUserService.insertByBo(user));
}

2. 多个权限(满足其一即可)

@SaCheckPermission(value = {"system:user:add", "system:user:edit"}, mode = SaMode.OR)
@PostMapping("/save")
public R<Void> save(@RequestBody SysUserBo user) {
    // 只要有 add 或 edit 任一权限即可访问
}

3. 多个权限(必须全部满足)

@SaCheckPermission(value = {"system:user:add", "system:user:export"}, mode = SaMode.AND)
@PostMapping("/addAndExport")
public R<Void> addAndExport(@RequestBody SysUserBo user) {
    // 必须同时拥有 add 和 export 两个权限
}

四、在你的项目中的实际应用

从代码中可以看到,你的项目大量使用了这个注解:

// 示例1:查询列表 - 需要 list 权限
@SaCheckPermission("workflow:category:list")
@GetMapping("/list")
public R<List<FlowCategoryVo>> list(FlowCategoryBo bo) {
    List<FlowCategoryVo> list = flwCategoryService.queryList(bo);
    return R.ok(list);
}

// 示例2:新增操作 - 需要 add 权限
@SaCheckPermission("workflow:category:add")
@Log(title = "流程分类", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody FlowCategoryBo category) {
    if (!flwCategoryService.checkCategoryNameUnique(category)) {
        return R.fail("新增流程分类失败,名称已存在");
    }
    return toAjax(flwCategoryService.insertByBo(category));
}

// 示例3:修改操作 - 需要 edit 权限
@SaCheckPermission("workflow:category:edit")
@Log(title = "流程分类", businessType = BusinessType.UPDATE)
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody FlowCategoryBo category) {
    return toAjax(flwCategoryService.updateByBo(category));
}

// 示例4:删除操作 - 需要 remove 权限
@SaCheckPermission("workflow:category:remove")
@Log(title = "流程分类", businessType = BusinessType.DELETE)
@DeleteMapping("/{categoryId}")
public R<Void> remove(@PathVariable Long categoryId) {
    return toAjax(flwCategoryService.deleteById(categoryId));
}

// 示例5:导出操作 - 需要 export 权限
@SaCheckPermission("workflow:category:export")
@Log(title = "流程分类", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(FlowCategoryBo bo, HttpServletResponse response) {
    List<FlowCategoryVo> list = flwCategoryService.queryList(bo);
    ExcelUtil.exportExcel(list, "流程分类", FlowCategoryVo.class, response);
}

五、工作原理

1. 权限校验流程

请求到达 → 拦截器检测注解 → 获取当前登录用户 
→ 调用 StpInterface.getPermissionList() 获取用户权限列表 
→ 判断是否包含所需权限 
→ 通过/拒绝

2. 权限数据来源

在你的项目中,权限数据通过 SaPermissionImpl 类获取:

public class SaPermissionImpl implements StpInterface {
    
    /**
     * 获取用户的权限列表
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        
        // 从登录用户信息中获取菜单权限
        if (CollUtil.isNotEmpty(loginUser.getMenuPermission())) {
            return new ArrayList<>(loginUser.getMenuPermission());
        } else {
            return new ArrayList<>();
        }
    }
    
    /**
     * 获取用户的角色列表
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        LoginUser loginUser = LoginHelper.getLoginUser();
        
        if (CollUtil.isNotEmpty(loginUser.getRolePermission())) {
            return new ArrayList<>(loginUser.getRolePermission());
        } else {
            return new ArrayList<>();
        }
    }
}

3. 配置信息

application-common.yml 中配置了 Sa-Token 的基本参数:

sa-token:
  token-name: Authorization          # Token 名称
  check-same-token: true             # 开启内网服务调用鉴权
  is-concurrent: true                # 允许同一账号并发登录
  is-share: false                    # 每次登录新建一个 token
  jwt-secret-key: abcdefghijklmnopqrstuvwxyz  # JWT 密钥

六、权限标识命名规范

你的项目采用**三段式命名规范**:

模块:功能:操作

常见示例:
- system:user:list - 系统管理-用户管理-查询
- system:user:add - 系统管理-用户管理-新增
- system:user:edit - 系统管理-用户管理-修改
- system:user:remove - 系统管理-用户管理-删除
- system:user:export - 系统管理-用户管理-导出
- workflow:category:query - 工作流-分类管理-详情查询


七、与其他注解的组合使用

通常会配合以下注解一起使用:

@SaCheckPermission("system:oss:upload")      // 权限校验
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)  // 操作日志
@RepeatSubmit()                               // 防重复提交
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) {
    SysOssVo oss = iSysOssService.upload(file);
    // ...
}

八、异常处理

当权限校验失败时,会抛出 NotLoginException 或权限不足异常,由全局异常处理器统一处理:

@Bean
public SaTokenExceptionHandler saTokenExceptionHandler() {
    return new SaTokenExceptionHandler();
}

返回给前端的通常是:

{
  "code": 403,
  "msg": "没有权限,请联系管理员授权"
}

九、前端权限控制

前端也有对应的权限校验工具函数(permission.ts):

export const checkPermi = (value: any) => {
  if (value && value instanceof Array && value.length > 0) {
    const permissions = useUserStore().permissions;
    const permissionDatas = value;
    const all_permission = '*:*:*';  // 超级管理员权限

    const hasPermission = permissions.some((permission) => {
      return all_permission === permission || permissionDatas.includes(permission);
    });

    return hasPermission;
  }
  return false;
};

前端使用:

<el-button 
  v-if="checkPermi(['system:user:add'])"
  @click="handleAdd">
  新增
</el-button>

十、最佳实践

  1. 最小权限原则:只授予完成工作所需的最小权限
  2. 统一命名规范:遵循 模块:功能:操作 的命名方式
  3. 关键操作必加:增删改等敏感操作必须添加权限校验
  4. 前后端双重校验:前端隐藏按钮 + 后端接口校验
  5. 特殊权限标识*:*:* 表示超级管理员,拥有所有权限

总结

@SaCheckPermission 是一个强大且易用的权限控制注解,它通过 AOP 切面实现无侵入式的权限校验,让你的业务代码更加简洁清晰。在你的 RuoYi-Cloud-Plus 项目中,它是整个 RBAC(基于角色的访问控制)权限体系的核心组件之一。