Skip to content

权限调试指南

常见问题

1. 菜单不显示

可能原因

  1. 用户没有菜单权限
  2. 菜单被禁用
  3. 路由配置错误
  4. 菜单类型不正确

排查步骤

typescript
// 1. 检查用户权限
import { useUserStore } from '@/store/modules/user'

const userStore = useUserStore()
console.log('用户权限:', userStore.permissions)
console.log('用户角色:', userStore.roles)

// 2. 检查菜单数据
const menuStore = usePermissionStore()
console.log('菜单路由:', menuStore.routes)

// 3. 检查后端返回的菜单
// 在浏览器 Network 标签查看 GET /api/v1/menus/routes 响应

解决方案

sql
-- 检查菜单是否启用
SELECT * FROM sys_menu WHERE status = 1;

-- 检查用户角色
SELECT * FROM sys_user_role WHERE user_id = ?;

-- 检查角色菜单
SELECT * FROM sys_role_menu WHERE role_id = ?;

2. 按钮不显示

可能原因

  1. 用户没有按钮权限
  2. 权限标识配置错误
  3. v-hasPerm 指令使用错误

排查步骤

typescript
// 1. 检查权限标识
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()
console.log('用户权限列表:', userStore.permissions)

// 2. 检查按钮权限标识
// 如 v-hasPerm="['sys:user:add']"
// 确认 'sys:user:add' 在 userStore.permissions 中

解决方案

vue
<!-- 临时调试:显示所有按钮 -->
<el-button v-if="true">新增</el-button>

<!-- 正确方式 -->
<el-button v-hasPerm="['sys:user:add']">新增</el-button>

3. 接口返回 403

可能原因

  1. Token 失效
  2. 权限不足
  3. 接口权限配置错误

排查步骤

typescript
// 1. 检查 Token
const token = localStorage.getItem('accessToken')
console.log('Token:', token)

// 2. 解析 Token(使用 jwt.io)
// 查看过期时间和用户信息

// 3. 检查请求头
// Network 标签 → Request Headers → Authorization

解决方案

java
// 后端检查权限
@PreAuthorize("hasPermission('sys:user:list')")
public List<User> list() {
    // ...
}

// 确保 Token 有效且用户有对应权限

4. 动态路由不生效

可能原因

  1. 路由守卫逻辑错误
  2. 路由格式不正确
  3. router.addRoute 调用时机错误

排查步骤

typescript
// src/permission.ts
router.beforeEach(async (to, from, next) => {
  // 1. 打印路由信息
  console.log('目标路由:', to)
  console.log('已注册路由:', router.getRoutes().map(r => r.path))
  
  // 2. 检查是否已加载路由
  if (!permissionStore.isRoutesLoaded) {
    // 加载路由
    const routes = await permissionStore.generateRoutes()
    routes.forEach(route => {
      router.addRoute(route)
    })
    next({ ...to, replace: true })
  }
})

解决方案

typescript
// 确保路由格式正确
const route = {
  path: '/system/user',
  name: 'SystemUser',
  component: () => import('@/views/system/user/index.vue'),
  meta: {
    title: '用户管理',
    icon: 'user',
    permissions: ['sys:user:list']
  }
}

// 使用 router.addRoute
router.addRoute(route)

调试工具

1. 浏览器 DevTools

Application → Local Storage

accessToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
refreshToken: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Vue DevTools

  • 检查 Pinia Store 状态
  • 检查组件权限指令

2. 后端日志

启用权限日志:

yaml
# application.yml
logging:
  level:
    com.youlai.auth: DEBUG
    org.springframework.security: DEBUG

查看日志:

log
2024-01-15 10:30:00 DEBUG [AuthorizationFilter] - Checking permission: sys:user:list
2024-01-15 10:30:00 DEBUG [AuthorizationFilter] - User permissions: [sys:user:list, sys:user:add]
2024-01-15 10:30:00 DEBUG [AuthorizationFilter] - Permission granted

3. 数据库查询

sql
-- 查询用户权限
SELECT 
    u.username,
    r.role_name,
    m.permission
FROM sys_user u
JOIN sys_user_role ur ON u.id = ur.user_id
JOIN sys_role r ON ur.role_id = r.id
JOIN sys_role_menu rm ON r.id = rm.role_id
JOIN sys_menu m ON rm.menu_id = m.id
WHERE u.username = 'admin';

-- 查询菜单树
SELECT 
    id,
    parent_id,
    name,
    path,
    permission
FROM sys_menu
WHERE status = 1
ORDER BY sort;

权限配置检查清单

前端检查

  • [ ] Token 是否正确存储
  • [ ] 请求头是否携带 Authorization
  • [ ] 权限指令是否正确使用
  • [ ] 路由守卫逻辑是否正确

后端检查

  • [ ] 用户是否有对应角色
  • [ ] 角色是否有对应菜单权限
  • [ ] 菜单权限标识是否正确
  • [ ] 接口是否添加权限注解

数据库检查

  • [ ] sys_user_role 表数据是否正确
  • [ ] sys_role_menu 表数据是否正确
  • [ ] sys_menu 表菜单是否启用
  • [ ] 权限标识是否唯一

常见错误示例

错误 1:权限标识不一致

前端

vue
<el-button v-hasPerm="['sys:user:add']">新增</el-button>

后端

java
@PreAuthorize("hasPermission('sys:user:create')") // 不一致
public void add() {}

解决:统一使用 sys:user:add

错误 2:菜单类型配置错误

sql
-- 按钮类型菜单的 parent_id 应该指向父菜单
INSERT INTO sys_menu (name, type, parent_id, permission)
VALUES ('新增用户', 3, 1, 'sys:user:add');
--              ↑     ↑
--          按钮类型  父菜单 ID

错误 3:超级管理员判断错误

typescript
// 错误:硬编码角色名称
if (userStore.roles.includes('admin')) {
  // ...
}

// 正确:使用角色标识
if (userStore.roles.includes('ADMIN')) {
  // ...
}

临时解决方案

如果权限系统有问题,可以临时禁用:

typescript
// src/utils/permission.ts
export function checkPermission(permissions: string[]): boolean {
  // 临时:返回 true
  return true
  
  // 原逻辑
  // return permissions.some(p => userStore.permissions.includes(p))
}

⚠️ 注意:生产环境必须启用权限控制!

相关链接

基于 MIT 许可发布 · 由 ❤️ 和 ☕ 驱动 · 支持作者