通过 VITE_AUTO_FILL_TEST 环境变量控制,在 .env.test 中启用, 使测试环境构建后登录框也能自动填充测试账号,方便测试人员使用。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
379 lines
8.3 KiB
Markdown
379 lines
8.3 KiB
Markdown
# 前端权限控制使用指南
|
||
|
||
## 📋 概述
|
||
|
||
前端权限控制系统已经完善,支持:
|
||
|
||
- ✅ 自动获取用户信息(刷新页面时)
|
||
- ✅ 角色权限检查
|
||
- ✅ 权限码检查
|
||
- ✅ 路由守卫自动验证
|
||
|
||
## 🔧 已修复的问题
|
||
|
||
### 1. **认证状态判断**
|
||
|
||
- **之前**: `isAuthenticated = !!token && !!user`(刷新页面时 user 为 null 导致判断失败)
|
||
- **现在**: `isAuthenticated = !!token`(只要有 token 就认为已认证)
|
||
|
||
### 2. **自动获取用户信息**
|
||
|
||
- **之前**: 刷新页面后用户信息丢失
|
||
- **现在**:
|
||
- 应用启动时自动获取(`main.ts`)
|
||
- 路由守卫中自动获取(如果 token 存在但 user 不存在)
|
||
|
||
### 3. **权限检查**
|
||
|
||
- **之前**: 只检查 `requiresAuth`,没有角色和权限检查
|
||
- **现在**: 支持角色和权限检查
|
||
|
||
## 🎯 使用方法
|
||
|
||
### 1. 在路由中配置权限
|
||
|
||
#### 使用角色控制
|
||
|
||
```typescript
|
||
{
|
||
path: "users",
|
||
name: "SystemUsers",
|
||
component: () => import("@/views/system/users/Index.vue"),
|
||
meta: {
|
||
title: "用户管理",
|
||
requiresAuth: true,
|
||
roles: ["super_admin", "admin"], // 需要 super_admin 或 admin 角色
|
||
},
|
||
}
|
||
```
|
||
|
||
#### 使用权限控制
|
||
|
||
```typescript
|
||
{
|
||
path: "users",
|
||
name: "SystemUsers",
|
||
component: () => import("@/views/system/users/Index.vue"),
|
||
meta: {
|
||
title: "用户管理",
|
||
requiresAuth: true,
|
||
permissions: ["user:read"], // 需要 user:read 权限
|
||
},
|
||
}
|
||
```
|
||
|
||
#### 同时使用角色和权限
|
||
|
||
```typescript
|
||
{
|
||
path: "users",
|
||
name: "SystemUsers",
|
||
component: () => import("@/views/system/users/Index.vue"),
|
||
meta: {
|
||
title: "用户管理",
|
||
requiresAuth: true,
|
||
roles: ["admin"], // 需要 admin 角色
|
||
permissions: ["user:read"], // 并且需要 user:read 权限
|
||
},
|
||
}
|
||
```
|
||
|
||
**注意**: 如果同时设置了 `roles` 和 `permissions`,需要**同时满足**两者。
|
||
|
||
### 2. 在组件中使用权限
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
<!-- 根据角色显示 -->
|
||
<a-button v-if="authStore.hasRole('super_admin')" @click="deleteAll">
|
||
删除所有
|
||
</a-button>
|
||
|
||
<!-- 根据权限显示 -->
|
||
<a-button v-if="authStore.hasPermission('user:create')" @click="createUser">
|
||
创建用户
|
||
</a-button>
|
||
|
||
<!-- 检查多个角色 -->
|
||
<a-button
|
||
v-if="authStore.hasAnyRole(['admin', 'editor'])"
|
||
@click="editUser"
|
||
>
|
||
编辑用户
|
||
</a-button>
|
||
|
||
<!-- 检查多个权限 -->
|
||
<a-button
|
||
v-if="authStore.hasAnyPermission(['user:update', 'user:delete'])"
|
||
@click="manageUser"
|
||
>
|
||
管理用户
|
||
</a-button>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { useAuthStore } from "@/stores/auth";
|
||
|
||
const authStore = useAuthStore();
|
||
</script>
|
||
```
|
||
|
||
### 3. Store 方法说明
|
||
|
||
#### `hasRole(role: string): boolean`
|
||
|
||
检查用户是否有指定角色
|
||
|
||
```typescript
|
||
if (authStore.hasRole("super_admin")) {
|
||
// 用户是超级管理员
|
||
}
|
||
```
|
||
|
||
#### `hasPermission(permission: string): boolean`
|
||
|
||
检查用户是否有指定权限
|
||
|
||
```typescript
|
||
if (authStore.hasPermission("user:create")) {
|
||
// 用户可以创建用户
|
||
}
|
||
```
|
||
|
||
#### `hasAnyRole(roles: string[]): boolean`
|
||
|
||
检查用户是否有任一指定角色
|
||
|
||
```typescript
|
||
if (authStore.hasAnyRole(["admin", "editor"])) {
|
||
// 用户是 admin 或 editor
|
||
}
|
||
```
|
||
|
||
#### `hasAnyPermission(permissions: string[]): boolean`
|
||
|
||
检查用户是否有任一指定权限
|
||
|
||
```typescript
|
||
if (authStore.hasAnyPermission(["user:create", "user:update"])) {
|
||
// 用户可以创建或更新用户
|
||
}
|
||
```
|
||
|
||
## 🔄 工作流程
|
||
|
||
### 1. 应用启动流程
|
||
|
||
```
|
||
应用启动
|
||
↓
|
||
检查 localStorage 中是否有 token
|
||
↓
|
||
如果有 token,调用 fetchUserInfo() 获取用户信息
|
||
↓
|
||
用户信息包含 roles 和 permissions
|
||
↓
|
||
应用挂载完成
|
||
```
|
||
|
||
### 2. 路由导航流程
|
||
|
||
```
|
||
用户访问路由
|
||
↓
|
||
路由守卫 beforeEach
|
||
↓
|
||
检查 token 是否存在
|
||
↓
|
||
如果 token 存在但 user 不存在 → 自动获取用户信息
|
||
↓
|
||
检查 requiresAuth → 是否需要登录
|
||
↓
|
||
检查 roles → 是否有指定角色
|
||
↓
|
||
检查 permissions → 是否有指定权限
|
||
↓
|
||
允许/拒绝访问
|
||
```
|
||
|
||
### 3. 登录流程
|
||
|
||
```
|
||
用户登录
|
||
↓
|
||
调用 authApi.login()
|
||
↓
|
||
返回 token 和 user 信息(包含 roles 和 permissions)
|
||
↓
|
||
保存 token 到 localStorage
|
||
↓
|
||
保存 user 到 store
|
||
↓
|
||
跳转到目标页面
|
||
```
|
||
|
||
## 📝 路由配置示例
|
||
|
||
### 完整的路由配置示例
|
||
|
||
```typescript
|
||
const routes: RouteRecordRaw[] = [
|
||
{
|
||
path: "/login",
|
||
name: "Login",
|
||
component: () => import("@/views/auth/Login.vue"),
|
||
meta: { requiresAuth: false },
|
||
},
|
||
{
|
||
path: "/",
|
||
component: () => import("@/layouts/BasicLayout.vue"),
|
||
redirect: "/workbench",
|
||
meta: { requiresAuth: true },
|
||
children: [
|
||
{
|
||
path: "workbench",
|
||
name: "workbench",
|
||
component: () => import("@/views/workbench/Index.vue"),
|
||
meta: {
|
||
title: "仪表盘",
|
||
requiresAuth: true,
|
||
},
|
||
},
|
||
{
|
||
path: "system/users",
|
||
name: "SystemUsers",
|
||
component: () => import("@/views/system/users/Index.vue"),
|
||
meta: {
|
||
title: "用户管理",
|
||
requiresAuth: true,
|
||
permissions: ["user:read"], // 需要查看用户权限
|
||
},
|
||
},
|
||
{
|
||
path: "system/roles",
|
||
name: "SystemRoles",
|
||
component: () => import("@/views/system/roles/Index.vue"),
|
||
meta: {
|
||
title: "角色管理",
|
||
requiresAuth: true,
|
||
roles: ["super_admin", "admin"], // 需要管理员角色
|
||
},
|
||
},
|
||
],
|
||
},
|
||
];
|
||
```
|
||
|
||
## 🎨 实际应用场景
|
||
|
||
### 场景 1: 根据权限显示菜单
|
||
|
||
```vue
|
||
<template>
|
||
<a-menu>
|
||
<a-menu-item v-if="authStore.hasPermission('user:read')">
|
||
<router-link to="/system/users">用户管理</router-link>
|
||
</a-menu-item>
|
||
<a-menu-item v-if="authStore.hasRole('admin')">
|
||
<router-link to="/system/roles">角色管理</router-link>
|
||
</a-menu-item>
|
||
</a-menu>
|
||
</template>
|
||
```
|
||
|
||
### 场景 2: 根据权限显示按钮
|
||
|
||
```vue
|
||
<template>
|
||
<a-table>
|
||
<template #action="{ record }">
|
||
<a-space>
|
||
<a-button
|
||
v-if="authStore.hasPermission('user:update')"
|
||
@click="edit(record)"
|
||
>
|
||
编辑
|
||
</a-button>
|
||
<a-button
|
||
v-if="authStore.hasPermission('user:delete')"
|
||
danger
|
||
@click="remove(record)"
|
||
>
|
||
删除
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
</a-table>
|
||
</template>
|
||
```
|
||
|
||
### 场景 3: 组合权限检查
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 需要同时满足角色和权限 -->
|
||
<a-button
|
||
v-if="authStore.hasRole('admin') && authStore.hasPermission('user:delete')"
|
||
@click="deleteAll"
|
||
>
|
||
批量删除
|
||
</a-button>
|
||
</template>
|
||
```
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
### 1. 路由守卫是异步的
|
||
|
||
路由守卫使用了 `async/await`,确保在检查权限前用户信息已加载。
|
||
|
||
### 2. 权限检查顺序
|
||
|
||
1. 认证检查(`requiresAuth`)
|
||
2. 角色检查(`roles`)
|
||
3. 权限检查(`permissions`)
|
||
|
||
### 3. 403 页面
|
||
|
||
如果没有权限,会跳转到 `/403` 页面。你可以自定义这个页面。
|
||
|
||
### 4. 权限更新
|
||
|
||
如果用户权限发生变化,需要:
|
||
|
||
- 重新登录,或
|
||
- 调用 `authStore.fetchUserInfo()` 刷新用户信息
|
||
|
||
## 🔍 调试技巧
|
||
|
||
### 1. 查看当前用户信息
|
||
|
||
```typescript
|
||
import { useAuthStore } from "@/stores/auth";
|
||
|
||
const authStore = useAuthStore();
|
||
console.log("用户信息:", authStore.user);
|
||
console.log("角色:", authStore.user?.roles);
|
||
console.log("权限:", authStore.user?.permissions);
|
||
```
|
||
|
||
### 2. 检查权限
|
||
|
||
```typescript
|
||
console.log("是否有 admin 角色:", authStore.hasRole("admin"));
|
||
console.log("是否有 user:create 权限:", authStore.hasPermission("user:create"));
|
||
```
|
||
|
||
## 📚 总结
|
||
|
||
现在权限控制系统已经完善:
|
||
|
||
1. ✅ **自动获取用户信息** - 刷新页面不会丢失
|
||
2. ✅ **路由权限检查** - 支持角色和权限控制
|
||
3. ✅ **组件权限检查** - 可以在组件中使用权限方法
|
||
4. ✅ **类型安全** - TypeScript 类型定义完善
|
||
|
||
可以开始使用权限控制功能了!
|