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

发布于:2025-09-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

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

权限控制与注解使用

前言

在前面的文章中,我们了解了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")
    @PostMapping
    public 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")
    @PutMapping
    public 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 {
    
    @Autowired
    private ISysUserService userService;
    
    @Autowired
    private ISysRoleService roleService;
    
    @Autowired
    private ISysMenuService menuService;
    
    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public 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);
    }
    
    /**
     * 返回一个账号所拥有的角色标识集合
     */
    @Override
    public 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的高级特性,包括监听器机制、拦截器配置等内容。

参考资料


网站公告

今日签到

点亮在社区的每一天
去签到