当前位置: 首页 > news >正文

Ruoyi-vue-plus-5.x第一篇Sa-Token权限认证体系深度解析:1.3 权限控制与注解使用

👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高教辅资料推荐等,欢迎关注交流!🚀

权限控制与注解使用

前言

在前面的文章中,我们了解了Sa-Token的基础概念和登录认证机制。本文将深入探讨Sa-Token的权限控制体系,重点介绍各种权限注解的使用方法、复合权限表达式的应用,以及在RuoYi-Vue-Plus中的实际应用场景。

Sa-Token权限注解体系

1. @SaCheckLogin - 登录校验注解

@SaCheckLogin是最基础的权限注解,用于校验用户是否已登录。

@RestController
@RequestMapping("/user")
public class UserController {/*** 获取用户信息 - 需要登录*/@SaCheckLogin@GetMapping("/info")public R<UserInfoVo> getUserInfo() {Object loginId = StpUtil.getLoginId();SysUser user = userService.selectUserById(Long.valueOf(loginId.toString()));return R.ok(BeanUtil.toBean(user, UserInfoVo.class));}/*** 修改个人信息 - 需要登录*/@SaCheckLogin@PutMapping("/profile")public R<Void> updateProfile(@RequestBody UserProfileDto profileDto) {Object loginId = StpUtil.getLoginId();return toAjax(userService.updateUserProfile(Long.valueOf(loginId.toString()), profileDto));}
}

2. @SaCheckRole - 角色校验注解

@SaCheckRole用于校验用户是否拥有指定角色。

@RestController
@RequestMapping("/admin")
public class AdminController {/*** 管理员专用接口 - 需要admin角色*/@SaCheckRole("admin")@GetMapping("/dashboard")public R<DashboardVo> getDashboard() {return R.ok(adminService.getDashboardData());}/*** 系统配置 - 需要admin或system角色*/@SaCheckRole(value = {"admin", "system"}, mode = SaMode.OR)@GetMapping("/config")public R<SystemConfigVo> getSystemConfig() {return R.ok(configService.getSystemConfig());}/*** 高级设置 - 需要同时拥有admin和security角色*/@SaCheckRole(value = {"admin", "security"}, mode = SaMode.AND)@PostMapping("/security/settings")public R<Void> updateSecuritySettings(@RequestBody SecuritySettingsDto settings) {return toAjax(securityService.updateSettings(settings));}
}

3. @SaCheckPermission - 权限校验注解

@SaCheckPermission是最常用的权限注解,用于校验用户是否拥有指定权限。

@RestController
@RequestMapping("/system/user")
public class SysUserController {/*** 查询用户列表*/@SaCheckPermission("system:user:list")@GetMapping("/list")public TableDataInfo<SysUserVo> list(SysUserBo user, PageQuery pageQuery) {return userService.selectPageUserList(user, pageQuery);}/*** 新增用户*/@SaCheckPermission("system:user:add")@PostMappingpublic R<Void> add(@Validated @RequestBody SysUserBo user) {// 检查用户名是否重复if (!userService.checkUserNameUnique(user)) {return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");}return toAjax(userService.insertUser(user));}/*** 修改用户*/@SaCheckPermission("system:user:edit")@PutMappingpublic R<Void> edit(@Validated @RequestBody SysUserBo user) {userService.checkUserAllowed(user.getUserId());if (!userService.checkUserNameUnique(user)) {return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");}return toAjax(userService.updateUser(user));}/*** 删除用户*/@SaCheckPermission("system:user:remove")@DeleteMapping("/{userIds}")public R<Void> remove(@PathVariable Long[] userIds) {if (ArrayUtil.contains(userIds, getUserId())) {return R.fail("当前用户不能删除");}return toAjax(userService.deleteUserByIds(userIds));}/*** 重置密码*/@SaCheckPermission("system:user:resetPwd")@PutMapping("/resetPwd")public R<Void> resetPwd(@RequestBody SysUserBo user) {userService.checkUserAllowed(user.getUserId());user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));return toAjax(userService.resetUserPwd(user.getUserId(), user.getPassword()));}/*** 状态修改*/@SaCheckPermission("system:user:edit")@PutMapping("/changeStatus")public R<Void> changeStatus(@RequestBody SysUserBo user) {userService.checkUserAllowed(user.getUserId());return toAjax(userService.updateUserStatus(user.getUserId(), user.getStatus()));}
}

4. @SaCheckSafe - 二级认证注解

@SaCheckSafe用于需要二级认证的敏感操作。

@RestController
@RequestMapping("/system/security")
public class SecurityController {/*** 修改系统关键配置 - 需要二级认证*/@SaCheckSafe@PostMapping("/critical/config")public R<Void> updateCriticalConfig(@RequestBody CriticalConfigDto config) {return toAjax(securityService.updateCriticalConfig(config));}/*** 删除重要数据 - 需要二级认证*/@SaCheckSafe(value = "delete-data", timeout = 300)@DeleteMapping("/important/data/{id}")public R<Void> deleteImportantData(@PathVariable Long id) {return toAjax(dataService.deleteImportantData(id));}/*** 进行二级认证*/@PostMapping("/safe/check")public R<Void> safeCheck(@RequestBody SafeCheckDto safeCheckDto) {// 验证密码或其他认证方式if (validateSafeCheck(safeCheckDto)) {// 通过二级认证StpUtil.openSafe(safeCheckDto.getService(), safeCheckDto.getTimeout());return R.ok();}return R.fail("二级认证失败");}
}

5. @SaCheckBasic - HTTP Basic认证

@SaCheckBasic用于HTTP Basic认证场景。

@RestController
@RequestMapping("/api/external")
public class ExternalApiController {/*** 外部API接口 - 使用HTTP Basic认证*/@SaCheckBasic(realm = "External API", account = "api:123456")@PostMapping("/data/sync")public R<Void> syncData(@RequestBody DataSyncDto data) {return toAjax(externalService.syncData(data));}/*** 动态Basic认证*/@SaCheckBasic(realm = "Dynamic API")@PostMapping("/dynamic/api")public R<Void> dynamicApi(@RequestBody ApiRequestDto request) {// 在StpInterface中实现动态账号密码验证return toAjax(apiService.processRequest(request));}
}

6. @SaIgnore - 忽略校验注解

@SaIgnore用于忽略权限校验,通常用于公开接口。

@RestController
@RequestMapping("/public")
public class PublicController {/*** 公开接口 - 忽略所有权限校验*/@SaIgnore@GetMapping("/info")public R<SystemInfoVo> getSystemInfo() {return R.ok(systemService.getPublicInfo());}/*** 验证码接口 - 忽略权限校验*/@SaIgnore@GetMapping("/captcha")public R<CaptchaVo> getCaptcha() {return R.ok(captchaService.generateCaptcha());}
}

复合权限表达式

Sa-Token支持复杂的权限表达式,可以实现AND、OR等逻辑组合。

AND逻辑 - 同时拥有多个权限

@RestController
@RequestMapping("/finance")
public class FinanceController {/*** 财务报表 - 需要同时拥有查看和导出权限*/@SaCheckPermission(value = {"finance:report:view", "finance:report:export"}, mode = SaMode.AND)@GetMapping("/report/export")public R<Void> exportReport(@RequestParam String reportType) {return toAjax(financeService.exportReport(reportType));}/*** 审批操作 - 需要同时拥有审批权限和对应级别权限*/@SaCheckPermission(value = {"finance:approve:basic", "finance:approve:level2"}, mode = SaMode.AND)@PostMapping("/approve/level2")public R<Void> approveLevel2(@RequestBody ApprovalDto approval) {return toAjax(approvalService.approveLevel2(approval));}
}

OR逻辑 - 拥有任一权限即可

@RestController
@RequestMapping("/content")
public class ContentController {/*** 内容管理 - 拥有编辑或审核权限即可*/@SaCheckPermission(value = {"content:edit", "content:audit"}, mode = SaMode.OR)@PostMapping("/manage")public R<Void> manageContent(@RequestBody ContentDto content) {return toAjax(contentService.manageContent(content));}/*** 数据查看 - 管理员或数据分析师都可以查看*/@SaCheckRole(value = {"admin", "analyst"}, mode = SaMode.OR)@GetMapping("/data/view")public R<DataVo> viewData(@RequestParam String dataType) {return R.ok(dataService.getData(dataType));}
}

权限与角色混合校验

@RestController
@RequestMapping("/system/advanced")
public class AdvancedController {/*** 高级功能 - 需要管理员角色或特定权限*/@SaCheckPermission(value = "system:advanced:access")@SaCheckRole(value = "admin", mode = SaMode.OR)@GetMapping("/features")public R<List<FeatureVo>> getAdvancedFeatures() {return R.ok(featureService.getAdvancedFeatures());}
}

自定义权限验证逻辑

实现StpInterface接口

@Component
public class StpInterfaceImpl implements StpInterface {@Autowiredprivate ISysUserService userService;@Autowiredprivate ISysRoleService roleService;@Autowiredprivate ISysMenuService menuService;/*** 返回一个账号所拥有的权限码集合*/@Overridepublic List<String> getPermissionList(Object loginId, String loginType) {Long userId = Long.valueOf(loginId.toString());SysUser user = userService.selectUserById(userId);if (ObjectUtil.isNull(user)) {return Collections.emptyList();}// 管理员拥有所有权限if (user.isAdmin()) {return Arrays.asList("*:*:*");}// 获取用户权限列表Set<String> perms = menuService.selectMenuPermsByUserId(userId);// 添加动态权限perms.addAll(getDynamicPermissions(userId));return new ArrayList<>(perms);}/*** 返回一个账号所拥有的角色标识集合*/@Overridepublic List<String> getRoleList(Object loginId, String loginType) {Long userId = Long.valueOf(loginId.toString());SysUser user = userService.selectUserById(userId);if (ObjectUtil.isNull(user)) {return Collections.emptyList();}// 获取用户角色列表Set<String> roles = roleService.selectRolePermissionByUserId(userId);// 添加动态角色roles.addAll(getDynamicRoles(userId));return new ArrayList<>(roles);}/*** 获取动态权限*/private Set<String> getDynamicPermissions(Long userId) {Set<String> dynamicPerms = new HashSet<>();// 根据用户部门添加权限SysUser user = userService.selectUserById(userId);if (user.getDeptId() != null) {SysDept dept = deptService.selectDeptById(user.getDeptId());if (dept != null && "headquarters".equals(dept.getDeptType())) {dynamicPerms.add("system:headquarters:access");}}// 根据用户岗位添加权限List<SysPost> posts = postService.selectPostsByUserId(userId);for (SysPost post : posts) {if ("manager".equals(post.getPostCode())) {dynamicPerms.add("system:manager:access");}}return dynamicPerms;}/*** 获取动态角色*/private Set<String> getDynamicRoles(Long userId) {Set<String> dynamicRoles = new HashSet<>();// 根据业务逻辑动态分配角色// 例如:VIP用户自动获得vip角色if (isVipUser(userId)) {dynamicRoles.add("vip");}return dynamicRoles;}
}

自定义权限校验器

@Component
public class CustomPermissionChecker {/*** 检查数据权限*/public boolean checkDataPermission(String permission, Object dataId) {// 检查基础权限if (!StpUtil.hasPermission(permission)) {return false;}// 检查数据权限Object loginId = StpUtil.getLoginId();return dataPermissionService.hasDataAccess(Long.valueOf(loginId.toString()), dataId);}/*** 检查时间权限*/public boolean checkTimePermission(String permission, LocalTime startTime, LocalTime endTime) {if (!StpUtil.hasPermission(permission)) {return false;}LocalTime now = LocalTime.now();return now.isAfter(startTime) && now.isBefore(endTime);}/*** 检查IP权限*/public boolean checkIpPermission(String permission, String allowedIp) {if (!StpUtil.hasPermission(permission)) {return false;}String clientIp = ServletUtils.getClientIP();return allowedIp.equals(clientIp);}
}

权限注解的高级用法

条件权限校验

@RestController
@RequestMapping("/conditional")
public class ConditionalController {/*** 条件权限校验 - 根据参数动态校验权限*/@PostMapping("/operation")public R<Void> conditionalOperation(@RequestBody OperationDto operation) {// 根据操作类型动态校验权限String requiredPermission = "system:operation:" + operation.getType();StpUtil.checkPermission(requiredPermission);return toAjax(operationService.execute(operation));}/*** 分级权限校验*/@PostMapping("/level/{level}")public R<Void> levelOperation(@PathVariable Integer level, @RequestBody LevelOperationDto operation) {// 根据级别校验对应权限for (int i = 1; i <= level; i++) {StpUtil.checkPermission("system:level:" + i);}return toAjax(levelService.execute(operation, level));}
}

自定义注解

/*** 自定义数据权限注解*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {/*** 数据权限类型*/String value() default "";/*** 是否检查所有者权限*/boolean checkOwner() default false;/*** 是否检查部门权限*/boolean checkDept() default false;
}/*** 数据权限切面*/
@Aspect
@Component
public class DataPermissionAspect {@Around("@annotation(dataPermission)")public Object around(ProceedingJoinPoint point, DataPermission dataPermission) throws Throwable {// 检查基础登录StpUtil.checkLogin();// 获取当前用户Long userId = Long.valueOf(StpUtil.getLoginId().toString());// 检查数据权限if (dataPermission.checkOwner()) {checkOwnerPermission(point, userId);}if (dataPermission.checkDept()) {checkDeptPermission(point, userId);}return point.proceed();}private void checkOwnerPermission(ProceedingJoinPoint point, Long userId) {// 实现所有者权限检查逻辑}private void checkDeptPermission(ProceedingJoinPoint point, Long userId) {// 实现部门权限检查逻辑}
}

总结

本文详细介绍了Sa-Token的权限控制体系,包括:

  1. 基础注解:@SaCheckLogin、@SaCheckRole、@SaCheckPermission等
  2. 高级注解:@SaCheckSafe、@SaCheckBasic、@SaIgnore
  3. 复合表达式:AND、OR逻辑组合
  4. 自定义逻辑:StpInterface接口实现和自定义校验器
  5. 高级用法:条件权限、自定义注解等

Sa-Token的权限注解体系为RuoYi-Vue-Plus提供了灵活而强大的权限控制能力,能够满足各种复杂的业务场景需求。

在下一篇文章中,我们将探讨Sa-Token的高级特性,包括监听器机制、拦截器配置等内容。

参考资料

  • Sa-Token权限认证文档
  • Sa-Token注解鉴权文档
  • RuoYi-Vue-Plus权限管理源码
http://www.dtcms.com/a/358438.html

相关文章:

  • 【计算机组成原理】LRU计数器问题
  • Vue3 + GeoScene 地图点击事件系统设计
  • Selenium + PO 框架进阶实践:接入 Allure 报告与 Jenkins 持续集成
  • macOs上ffmpeg带入libx264库交叉编译
  • docker 启动一个clickhouse , docker 创建ck数据库
  • Python远程文件管理移动端适配与跨平台优化实战
  • vue3多个el-checkbox勾选框设置必选一个
  • 【OpenGL ES】光栅化插值原理和射线拾取原理
  • Day17(前端:JavaScript基础阶段)
  • Cocos游戏中自定义按钮组件(BtnEventComponent)的详细分析与实现
  • HAProxy 负载均衡全解析:从基础部署、负载策略到会话保持及性能优化指南
  • Spring : 事务管理
  • 音视频学习(六十一):H265中的VPS
  • Prompt Engineering:高效构建智能文本生成的策略与实践
  • 深层语义在自然语言处理中的理论框架与技术融合研究
  • AI大模型:(二)5.2 文生视频(Text-to-Video)模型训练实践
  • FPGA增量式方差与均值计算
  • 响应式编程框架Reactor【4】
  • FPGA学习笔记——SPI读写FLASH
  • 优化器全指南:从原理到调优实战
  • 原子操作与锁实现
  • 由于不对称GND过孔配置,差分信号过孔上的差模到共模转换
  • SQL相关知识 CTF SQL注入做题方法总结
  • seafile-setup-troubleshooting_# Seafile 安装与问题解决记录 # Seafile/Seahub 启动问题记录文档
  • Scikit-learn Python机器学习 - Scikit-learn加载数据集
  • C/C++:AddressSanitizer内存检测工具
  • 《以奋斗者为本》读书笔记(上篇:价值管理)
  • Ethan开发者创新项目日报 | 2025-08-30
  • MySQL之事务
  • 渲染数据列表:`map` 方法与 `key` 的奥秘