library-picturebook-activity/backend/docs/RBAC_EXAMPLES.md
2025-11-23 14:04:20 +08:00

445 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RBAC 权限控制使用示例
## 📋 目录
1. [基础使用](#基础使用)
2. [角色控制示例](#角色控制示例)
3. [权限控制示例](#权限控制示例)
4. [完整示例](#完整示例)
## 🔧 基础使用
### 1. 创建权限
```typescript
// 在数据库中创建权限
const permissions = [
{ code: 'user:create', resource: 'user', action: 'create', name: '创建用户' },
{ code: 'user:read', resource: 'user', action: 'read', name: '查看用户' },
{ code: 'user:update', resource: 'user', action: 'update', name: '更新用户' },
{ code: 'user:delete', resource: 'user', action: 'delete', name: '删除用户' },
{ code: 'role:create', resource: 'role', action: 'create', name: '创建角色' },
{ code: 'role:read', resource: 'role', action: 'read', name: '查看角色' },
];
for (const perm of permissions) {
await prisma.permission.create({ data: perm });
}
```
### 2. 创建角色并分配权限
```typescript
// 创建管理员角色
const adminRole = await prisma.role.create({
data: {
name: '管理员',
code: 'admin',
permissions: {
create: [
{ permission: { connect: { code: 'user:create' } } },
{ permission: { connect: { code: 'user:read' } } },
{ permission: { connect: { code: 'user:update' } } },
{ permission: { connect: { code: 'user:delete' } } },
{ permission: { connect: { code: 'role:create' } } },
{ permission: { connect: { code: 'role:read' } } },
]
}
}
});
// 创建编辑角色(只有查看和更新权限)
const editorRole = await prisma.role.create({
data: {
name: '编辑',
code: 'editor',
permissions: {
create: [
{ permission: { connect: { code: 'user:read' } } },
{ permission: { connect: { code: 'user:update' } } },
]
}
}
});
```
### 3. 给用户分配角色
```typescript
// 给用户分配管理员角色
await prisma.userRole.create({
data: {
user: { connect: { id: 1 } },
role: { connect: { code: 'admin' } }
}
});
// 用户可以有多个角色
await prisma.userRole.create({
data: {
user: { connect: { id: 1 } },
role: { connect: { code: 'editor' } }
}
});
```
## 🎯 角色控制示例
### 在控制器中使用角色装饰器
```typescript
import { Controller, Get, Post, Delete, UseGuards } from '@nestjs/common';
import { Roles } from '../auth/decorators/roles.decorator';
import { RolesGuard } from '../auth/guards/roles.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard) // 先验证 JWT再验证角色
export class UsersController {
// 所有已登录用户都可以查看
@Get()
findAll() {
return this.usersService.findAll();
}
// 只有管理员和编辑可以创建用户
@Post()
@Roles('admin', 'editor')
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
// 只有管理员可以删除用户
@Delete(':id')
@Roles('admin')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
```
## 🔐 权限控制示例
### 创建权限守卫(可选扩展)
```typescript
// src/auth/guards/permissions.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
'permissions',
[context.getHandler(), context.getClass()],
);
if (!requiredPermissions) {
return true; // 没有权限要求,允许访问
}
const { user } = context.switchToHttp().getRequest();
const userPermissions = user.permissions || [];
// 检查用户是否拥有任一所需权限
return requiredPermissions.some((permission) =>
userPermissions.includes(permission),
);
}
}
```
### 创建权限装饰器
```typescript
// src/auth/decorators/permissions.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const PERMISSIONS_KEY = 'permissions';
export const Permissions = (...permissions: string[]) =>
SetMetadata(PERMISSIONS_KEY, permissions);
```
### 使用权限控制
```typescript
import { Controller, Get, Post, Delete, UseGuards } from '@nestjs/common';
import { Permissions } from '../auth/decorators/permissions.decorator';
import { PermissionsGuard } from '../auth/guards/permissions.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
@Controller('users')
@UseGuards(JwtAuthGuard, PermissionsGuard)
export class UsersController {
@Get()
@Permissions('user:read') // 需要 user:read 权限
findAll() {
return this.usersService.findAll();
}
@Post()
@Permissions('user:create') // 需要 user:create 权限
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Delete(':id')
@Permissions('user:delete') // 需要 user:delete 权限
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
```
## 📚 完整示例
### 完整的用户管理控制器
```typescript
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Query,
UseGuards,
Request,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
@Controller('users')
@UseGuards(JwtAuthGuard) // 所有接口都需要登录
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// 查看用户列表 - 所有已登录用户都可以访问
@Get()
findAll(@Query('page') page?: string, @Query('pageSize') pageSize?: string) {
return this.usersService.findAll(
page ? parseInt(page) : 1,
pageSize ? parseInt(pageSize) : 10,
);
}
// 查看用户详情 - 所有已登录用户都可以访问
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
// 创建用户 - 需要 admin 或 editor 角色
@Post()
@UseGuards(RolesGuard)
@Roles('admin', 'editor')
create(@Body() createUserDto: CreateUserDto, @Request() req) {
// req.user 包含当前用户信息(从 JWT 中提取)
return this.usersService.create(createUserDto);
}
// 更新用户 - 需要 admin 角色,或者用户自己更新自己
@Patch(':id')
@UseGuards(RolesGuard)
async update(
@Param('id') id: string,
@Body() updateUserDto: UpdateUserDto,
@Request() req,
) {
const userId = parseInt(id);
const currentUserId = req.user.userId;
// 管理员可以更新任何人,普通用户只能更新自己
if (req.user.roles?.includes('admin') || userId === currentUserId) {
return this.usersService.update(userId, updateUserDto);
}
throw new ForbiddenException('无权更新此用户');
}
// 删除用户 - 只有管理员可以删除
@Delete(':id')
@UseGuards(RolesGuard)
@Roles('admin')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
```
## 🔍 权限检查流程
### 1. 用户登录
```typescript
// POST /api/auth/login
{
"username": "admin",
"password": "password123"
}
// 返回
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "admin",
"nickname": "管理员",
"roles": ["admin"], // 用户的角色列表
"permissions": [ // 用户的所有权限(从角色中聚合)
"user:create",
"user:read",
"user:update",
"user:delete",
"role:create",
"role:read"
]
}
}
```
### 2. 访问受保护的接口
```typescript
// 请求头
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
// 流程
1. JwtAuthGuard 验证 Token
└─> 提取用户信息,添加到 req.user
2. RolesGuard 检查角色
└─> req.user.roles 中检查是否包含所需角色
└─> 如果包含,允许访问;否则返回 403 Forbidden
```
## 🎨 前端权限控制示例
### Vue 3 中使用权限
```typescript
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null);
// 检查是否有指定角色
const hasRole = (role: string) => {
return user.value?.roles?.includes(role) ?? false;
};
// 检查是否有指定权限
const hasPermission = (permission: string) => {
return user.value?.permissions?.includes(permission) ?? false;
};
// 检查是否有任一角色
const hasAnyRole = (roles: string[]) => {
return roles.some(role => hasRole(role));
};
// 检查是否有任一权限
const hasAnyPermission = (permissions: string[]) => {
return permissions.some(perm => hasPermission(perm));
};
return {
user,
hasRole,
hasPermission,
hasAnyRole,
hasAnyPermission,
};
});
```
### 在组件中使用
```vue
<template>
<div>
<!-- 根据角色显示按钮 -->
<a-button v-if="authStore.hasRole('admin')" @click="deleteUser">
删除用户
</a-button>
<!-- 根据权限显示按钮 -->
<a-button v-if="authStore.hasPermission('user:create')" @click="createUser">
创建用户
</a-button>
<!-- 根据角色或权限显示 -->
<a-button
v-if="authStore.hasAnyRole(['admin', 'editor']) || authStore.hasPermission('user:update')"
@click="editUser"
>
编辑用户
</a-button>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from '@/stores/auth';
const authStore = useAuthStore();
</script>
```
### 路由守卫
```typescript
// router/index.ts
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();
// 检查是否需要认证
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
next({ name: 'Login' });
return;
}
// 检查角色
if (to.meta.roles && !authStore.hasAnyRole(to.meta.roles)) {
next({ name: 'Forbidden' });
return;
}
// 检查权限
if (to.meta.permissions && !authStore.hasAnyPermission(to.meta.permissions)) {
next({ name: 'Forbidden' });
return;
}
next();
});
```
## 📊 权限矩阵示例
| 角色 | user:create | user:read | user:update | user:delete | role:create | role:read |
|------|-------------|-----------|-------------|------------|-------------|-----------|
| admin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| editor | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| viewer | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
## 🎯 总结
RBAC 权限控制的核心是:
1. **用户** ←→ **角色** ←→ **权限**
2. 通过 `@Roles()` 装饰器控制接口访问
3. 前端根据返回的 `roles``permissions` 控制 UI 显示
4. 权限由 `resource:action` 组成,如 `user:create`
这样的设计既保证了安全性,又提供了良好的灵活性和可维护性!