👋 大家好,我是 阿问学长
!专注于分享优质开源项目
解析、毕业设计项目指导
支持、幼小初高
的教辅资料
推荐等,欢迎关注交流!🚀
权限控制与注解使用
前言
在前面的文章中,我们了解了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的权限控制体系,包括:
- 基础注解:@SaCheckLogin、@SaCheckRole、@SaCheckPermission等
- 高级注解:@SaCheckSafe、@SaCheckBasic、@SaIgnore
- 复合表达式:AND、OR逻辑组合
- 自定义逻辑:StpInterface接口实现和自定义校验器
- 高级用法:条件权限、自定义注解等
Sa-Token的权限注解体系为RuoYi-Vue-Plus提供了灵活而强大的权限控制能力,能够满足各种复杂的业务场景需求。
在下一篇文章中,我们将探讨Sa-Token的高级特性,包括监听器机制、拦截器配置等内容。