我来详细讲解一下 @SaCheckPermission 注解。
@SaCheckPermission 注解详解
一、基本概念
@SaCheckPermission 是 Sa-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>
十、最佳实践
- 最小权限原则:只授予完成工作所需的最小权限
- 统一命名规范:遵循
模块:功能:操作的命名方式 - 关键操作必加:增删改等敏感操作必须添加权限校验
- 前后端双重校验:前端隐藏按钮 + 后端接口校验
- 特殊权限标识:
*:*:*表示超级管理员,拥有所有权限
总结
@SaCheckPermission 是一个强大且易用的权限控制注解,它通过 AOP 切面实现无侵入式的权限校验,让你的业务代码更加简洁清晰。在你的 RuoYi-Cloud-Plus 项目中,它是整个 RBAC(基于角色的访问控制)权限体系的核心组件之一。