精确到按钮级别的权限认证
基础原理
按钮级权限控制不是锦上添花,而是保障系统安全和用户体验的刚需,原因在于:
- 数据安全与操作合规:不同角色的用户能执行的操作不同(比如普通员工只能查看数据,管理员能删除 / 编辑),如果仅做页面级权限,用户可能通过伪造请求(比如绕过前端手动调接口)执行越权操作,按钮级控制能从前端直接屏蔽非法操作入口,减少恶意请求。
- 提升用户体验:避免给用户展示看得见但点不了的按钮,或点击后提示无权限的尴尬场景,让界面只显示用户真正能操作的功能,降低使用困惑。
- 统一权限逻辑:前端和后端权限规则对齐,避免前端显示按钮但后端拒绝请求的不一致问题,减少前后端联调成本。
前端实现按钮级别的权限认证的核心逻辑:先获取用户权限标记集合 ->在渲染按钮时校验权限 -> 最终决定按钮是否显示/禁用。
前端优化
编写登录用户数据仓库(store/currentUser.js),存储当前登录用户权限标记列表:
javascript
import {ref, computed, reactive} from 'vue'
import { defineStore } from 'pinia'
export const useCurrentUserStore = defineStore('currentUser', () => {
// 登录用户信息实体
let currentUser = reactive({})
// 当前用户菜单列表
let currentMenu = reactive([])
// 当前用户权限标记
let currentPerms = reactive([])
// 设置登录用户信息实体
function setCurrentUser(currentUser) {
this.currentUser = currentUser
}
// 设置当前用户菜单列表
function setCurrentMenu(currentMenu) {
this.currentMenu = currentMenu
}
// 设置当前用户权限标记
function setCurrentPerms(currentPerms) {
this.currentPerms = currentPerms
}
function hasPerms(identifier) {
// 判断当前用户是否拥有指定权限标记
return this.currentPerms.includes(identifier)
}
return { currentUser, setCurrentUser, currentMenu, setCurrentMenu, currentPerms, setCurrentPerms, hasPerms }
})
编写权限工具类(plugins/PermsUtil.js),优化将用户权限标记存储到仓库中方法:
javascript
// 将用户权限菜单存储到仓库中
useCurrentUserStore().setCurrentMenu(toTreeList(response.data, false))
// 将用户权限标记存储到仓库中(包含所有类型的权限)
useCurrentUserStore().setCurrentPerms(response.data.map(perm => perm.identifier))
编写页面组件,完善按钮级别的权限认证:
html
<el-button type="warning" @click="showAddDialog" v-if="useCurrentUserStore().hasPerms('system:users:add')">
添加用户
</el-button>
在浏览器中测试:

后端 API 的权限认证
基础原理
参考文档:https://sa-token.cc/doc.html#/use/jur-auth、https://sa-token.cc/doc.html#/use/at-check
后端 API 是系统数据和业务操作的唯一入口,前端权限认证仅为体验层防护,后端权限认证才是真正的安全兜底,原因在于:
- 防越权操作:前端权限可被轻易绕过(比如通过 Apifox、抓包工具直接调用 API,或修改前端代码),如果后端不校验权限,恶意用户能执行删除数据、修改配置等高危操作,直接威胁系统安全。
- 符合业务规则:不同角色的用户本就该有不同操作范围(如普通员工只能查数据,管理员能删数据),API 权限认证是业务规则在后端的落地,确保什么人能做什么事。
- 降低系统风险:即使前端漏洞导致权限控制失效,后端的权限校验仍能拦截非法请求,避免单点漏洞引发全系统风险。
后端 API 实现权限认证的核心逻辑:登录生成身份凭证 Token -> 接口调用时校验凭证有效性并关联用户 -> 查询用户权限集合与 API 权限要求匹配 -> 匹配成功放行/失败拦截。
后端优化
编写权限认证是嫌累(cn.duozai.sadmin.config.StpInterfaceImpl),返回一个账号所拥有的权限码集合:
java
/**
* SaToken权限认证实现类
*/
@Component
public class StpInterfaceImpl implements StpInterface {
/**
* 返回一个账号所拥有的权限码集合
* @visduo
*
* @param loginId 登录id
* @param loginType 登录类型
* @return 权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 从会话中获取权限列表(获取当前登录用户权限列表时存入)
List<PermsEntity> permsList = (List<PermsEntity>) StpUtil.getSession().get("perms");
// 遍历权限列表,获取权限标识列表
List<String> identifierList = new ArrayList<>();
for (PermsEntity permsEntity : permsList) {
identifierList.add(permsEntity.getIdentifier());
}
// 返回权限标识列表
return identifierList;
}
/**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
* @visduo
*
* @param loginId 登录id
* @param loginType 登录类型
* @return 角色标识集合
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
return new ArrayList<>();
}
}
编写控制器,完善方法级别的权限认证:
java
@SaCheckPermission("system:users:add") // 注解校验
@Post
@Mapping("/add")
public ResponseResult add() {
// ...
}