首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >别再写死权限了!SpringBoot + Sa-Token 实现 RBAC 的最佳姿势

别再写死权限了!SpringBoot + Sa-Token 实现 RBAC 的最佳姿势

作者头像
程序员NEO
发布2026-04-29 19:35:57
发布2026-04-29 19:35:57
1530
举报

大家好,我是 NEO。

在做 Web 开发时,权限认证是一个绕不开的话题。它的核心逻辑其实非常简单:规定哪些用户可以访问哪些接口、页面或资源。

  • 管理员:访问正常,返回数据。
  • 普通用户:权限不足,拒绝访问。

虽然逻辑简单,但很多框架配置起来却异常繁琐。今天我们就来看看,如何使用 Sa-Token 这个轻量级框架,优雅地搞定权限认证。

核心原理:到底在校验什么?

框架是如何判断一个账号是否有权访问某个接口的呢?

从底层数据来看,逻辑是这样的:每个账号都拥有一组“权限码集合”,框架要做的,就是校验这个集合中是否包含当前接口要求的权限码。

  • • 如果有,通过。
  • • 如果没有,禁止访问!

所以,问题的核心就变成了两个:

  1. 1. 定义:当前账号拥有哪些权限码?
  2. 2. 校验:本次操作需要哪个权限码?

第一步:告诉框架你有哪些权限

在进行校验之前,你需要先实现 StpInterface 接口。这一步的作用是连接你的数据库,告诉 Sa-Token 指定账号到底拥有哪些权限。

新建一个类 StpInterfaceImpl,并注入到 Spring 容器中:

代码语言:javascript
复制
/**
 * 自定义权限加载接口实现类
 */
@Component // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {

    /**
     * 返回一个账号所拥有的权限码集合 
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 模拟数据,实际项目中请查询数据库
        List<String> list = new ArrayList<String>();    
        list.add("101");
        list.add("user.add");
        list.add("user.update");
        list.add("user.get");
        list.add("art.*"); // 支持通配符
        return list;
    }

    /**
     * 返回一个账号所拥有的角色标识集合
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 模拟数据
        List<String> list = new ArrayList<String>();    
        list.add("admin");
        list.add("super-admin");
        return list;
    }
}

参数解释:

  • • loginId:账号id,即你在调用 StpUtil.login(id) 时写入的唯一标识值。
  • • loginType:账号体系标识,此处可以暂时忽略,在 [ 多账户认证 ] 章节下会对这个概念做详细的解释。

有同学经常问的一个问题:

“我实现了这个接口,为什么程序启动时没执行?”

答: 这是正常的。该方法只有在每次调用鉴权代码时才会被执行,属于懒加载模式,不用担心性能浪费。

第二步:一行代码进行鉴权

准备工作做好了,现在可以在代码中进行鉴权了。Sa-Token 提供了极其简洁的 API:

1. 权限校验

代码语言:javascript
复制
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();

// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.add");        

// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException 
StpUtil.checkPermission("user.add");        

// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get");        

// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");    

扩展:NotPermissionException 异常对象可通过 getLoginType() 方法获取具体是哪个 StpLogic 抛出的异常

2. 角色校验

角色和权限是完全独立的,用法几乎一致:

代码语言:javascript
复制
// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();

// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("super-admin");        

// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");        

// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] 
StpUtil.checkRoleOr("super-admin", "shop-admin");        

扩展:NotRoleException 异常对象可通过 getLoginType() 方法获取具体是哪个 StpLogic 抛出的异常

3. 进阶:权限通配符

Sa-Token 支持强大的泛权限匹配。

例如,如果一个账号拥有 art.* 的权限,那么:

  • art.add -> 通过
  • art.delete -> 通过
  • goods.add -> 不通过

如果是超级管理员,可以直接给一个 * 权限,他将能够通过所有校验。

第三步:优雅地处理异常

鉴权失败时,框架会抛出 NotPermissionException。如果把这个异常直接展示给用户,体验非常差。

我们需要做一个全局异常拦截器,统一返回 JSON 格式的错误信息:

代码语言:javascript
复制
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    // 全局异常拦截 
    @ExceptionHandler
    public SaResult handlerException(Exception e) {
        e.printStackTrace(); 
        // 返回友好的错误提示
        return SaResult.error(e.getMessage());
    }
}

这样,前端收到的就是一个标准的 { code: 500, msg: "无此权限" },而不是一大堆报错堆栈。

扩展:如何精确控制到“按钮级”?

经常有需求问:“我不仅要拦截接口,还要控制页面上的按钮是否显示,该怎么做?”

这需要前后端配合完成:

  1. 1. 后端:登录时,把当前账号的所有权限码一次性返回给前端。
  2. 2. 前端:将权限码保存在 localStorageVuex/Pinia 中。
  3. 3. 显示控制:使用 JS 判断当前权限码列表中,是否包含按钮所需的权限。

如果是前后端一体项目,可以参考:Thymeleaf 标签方言[1],如果是前后端分离项目,则:

以 Vue 为例:

代码语言:javascript
复制
<!-- arr 是当前用户的权限码数组 -->
<div>
    <button v-if="arr.includes('user.get')">查询用户</button>
    <button v-if="arr.includes('user.update')">修改用户</button>
    <button v-if="arr.includes('user.delete')">删除按钮</button>
</div>

以上写法只为提供一个参考示例,不同框架有不同写法,大家可根据项目技术栈灵活封装进行调用。

特别提醒: 前端的鉴权只是为了提升用户体验(看不见的按钮点不了)。为了安全,后端接口必须再次进行 StpUtil.checkPermission() 校验,永远不要相信前端传来的请求!


写在最后

权限认证是后端系统的基石,Sa-Token 通过极简的设计,把这个复杂问题变得像写 if-else 一样简单。

如果你觉得这篇文章对你有帮助,欢迎点赞、在看、转发,这对我很重要!

我们下期见。

相关文章推荐

引用链接

[1] Thymeleaf 标签方言: https://sa-token.cc/doc.html#/plugin/thymeleaf-extend

如果这篇文章帮到了你,不妨点个分享给同样需要的朋友吧! 你的每一次支持,都是我持续创作的动力!💪

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员NEO 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 核心原理:到底在校验什么?
  • 第一步:告诉框架你有哪些权限
  • 第二步:一行代码进行鉴权
  • 第三步:优雅地处理异常
  • 扩展:如何精确控制到“按钮级”?
    • 引用链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档