273 lines
7.2 KiB
Markdown
273 lines
7.2 KiB
Markdown
# 用户权限API设计方案
|
||
|
||
## 1. 后端需要返回的数据结构
|
||
|
||
### 方案A:返回权限键数组(推荐)
|
||
|
||
**接口**: `GET /api/v3/users/{user_id}/permissions` 或集成在登录响应中
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "成功",
|
||
"data": {
|
||
"user_id": 123,
|
||
"role_id": 5,
|
||
"role_key": "provincial_admin",
|
||
"role_name": "省级管理员",
|
||
"permissions": [
|
||
"prompt_template:list:read",
|
||
"prompt_template:detail:read",
|
||
"prompt_template:create:write",
|
||
"prompt_template:update:write",
|
||
"prompt_template:delete:delete",
|
||
"document:list:read",
|
||
"document:create:write"
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 方案B:返回权限对象数组(更详细)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "成功",
|
||
"data": {
|
||
"user_id": 123,
|
||
"role_id": 5,
|
||
"role_key": "provincial_admin",
|
||
"role_name": "省级管理员",
|
||
"permissions": [
|
||
{
|
||
"permission_key": "prompt_template:create:write",
|
||
"module": "prompt_template",
|
||
"resource": "create",
|
||
"action": "write",
|
||
"display_name": "创建提示词模板"
|
||
},
|
||
{
|
||
"permission_key": "prompt_template:update:write",
|
||
"module": "prompt_template",
|
||
"resource": "update",
|
||
"action": "write",
|
||
"display_name": "更新提示词模板"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
## 2. 后端SQL查询示例
|
||
|
||
```sql
|
||
-- 获取用户的所有权限(通过角色关联)
|
||
SELECT DISTINCT
|
||
p.permission_key,
|
||
p.module,
|
||
p.resource,
|
||
p.action,
|
||
p.display_name
|
||
FROM users u
|
||
JOIN user_roles ur ON u.id = ur.user_id
|
||
JOIN role_permissions rp ON ur.role_id = rp.role_id AND rp.grant_type = 'GRANT'
|
||
JOIN permissions p ON rp.permission_id = p.id
|
||
WHERE u.id = $1
|
||
AND u.deleted_at IS NULL
|
||
AND ur.deleted_at IS NULL;
|
||
```
|
||
|
||
## 3. 集成到现有认证流程
|
||
|
||
### 选项1:集成到JWT Token中
|
||
- 优点:前端无需额外请求,直接从JWT解析
|
||
- 缺点:JWT体积变大,权限变更需要重新登录
|
||
- 适用场景:权限不经常变化,用户会话较短
|
||
|
||
### 选项2:集成到Session中
|
||
- 优点:权限可以实时更新(每次loader都会调用getUserSession)
|
||
- 缺点:每次页面加载都要查询数据库
|
||
- 适用场景:权限经常变化,需要实时控制
|
||
|
||
### 选项3:混合方案(推荐)
|
||
- JWT中存储基础权限(permission_key数组)
|
||
- 重要操作时后端再次验证
|
||
- 权限变更后强制用户重新登录或刷新token
|
||
|
||
## 4. 前端权限检查工具
|
||
|
||
### 创建权限Hook
|
||
|
||
```typescript
|
||
// app/hooks/usePermission.ts
|
||
import { useRouteLoaderData } from "@remix-run/react";
|
||
|
||
export function usePermission() {
|
||
const rootData = useRouteLoaderData("root") as {
|
||
permissions?: string[];
|
||
userRole: string;
|
||
};
|
||
|
||
const permissions = rootData?.permissions || [];
|
||
|
||
/**
|
||
* 检查是否有指定权限
|
||
* @param permissionKey 权限键,如 "prompt_template:create:write"
|
||
* @returns boolean
|
||
*/
|
||
const hasPermission = (permissionKey: string): boolean => {
|
||
return permissions.includes(permissionKey);
|
||
};
|
||
|
||
/**
|
||
* 检查是否有指定模块的任意权限
|
||
* @param module 模块名,如 "prompt_template"
|
||
* @returns boolean
|
||
*/
|
||
const hasModulePermission = (module: string): boolean => {
|
||
return permissions.some(p => p.startsWith(`${module}:`));
|
||
};
|
||
|
||
/**
|
||
* 检查是否有指定动作的权限
|
||
* @param module 模块名
|
||
* @param action 动作,如 "create", "update", "delete"
|
||
* @returns boolean
|
||
*/
|
||
const hasActionPermission = (module: string, action: string): boolean => {
|
||
return permissions.some(p =>
|
||
p.startsWith(`${module}:`) && p.includes(`:${action}`)
|
||
);
|
||
};
|
||
|
||
/**
|
||
* 批量检查权限(需要全部满足)
|
||
* @param permissionKeys 权限键数组
|
||
* @returns boolean
|
||
*/
|
||
const hasAllPermissions = (permissionKeys: string[]): boolean => {
|
||
return permissionKeys.every(key => permissions.includes(key));
|
||
};
|
||
|
||
/**
|
||
* 批量检查权限(满足任意一个即可)
|
||
* @param permissionKeys 权限键数组
|
||
* @returns boolean
|
||
*/
|
||
const hasAnyPermission = (permissionKeys: string[]): boolean => {
|
||
return permissionKeys.some(key => permissions.includes(key));
|
||
};
|
||
|
||
return {
|
||
permissions,
|
||
hasPermission,
|
||
hasModulePermission,
|
||
hasActionPermission,
|
||
hasAllPermissions,
|
||
hasAnyPermission
|
||
};
|
||
}
|
||
```
|
||
|
||
### 在组件中使用
|
||
|
||
```typescript
|
||
// app/routes/prompts._index.tsx
|
||
import { usePermission } from "~/hooks/usePermission";
|
||
|
||
export default function PromptsIndex() {
|
||
const { hasPermission, hasActionPermission } = usePermission();
|
||
|
||
// 检查是否有创建权限
|
||
const canCreate = hasPermission("prompt_template:create:write");
|
||
|
||
// 检查是否有编辑权限
|
||
const canEdit = hasPermission("prompt_template:update:write");
|
||
|
||
// 检查是否有删除权限
|
||
const canDelete = hasPermission("prompt_template:delete:delete");
|
||
|
||
// 或者使用通用检查
|
||
const canManage = hasActionPermission("prompt_template", "write");
|
||
|
||
return (
|
||
<div>
|
||
{canCreate && (
|
||
<Button onClick={() => navigate("/prompts/new")}>
|
||
新增提示词模板
|
||
</Button>
|
||
)}
|
||
|
||
{/* 表格操作列 */}
|
||
{canEdit && <button onClick={handleEdit}>编辑</button>}
|
||
{canDelete && <button onClick={handleDelete}>删除</button>}
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
## 5. 后端验证(双重保险)
|
||
|
||
前端权限检查只是为了改善用户体验,**后端必须再次验证权限**:
|
||
|
||
```typescript
|
||
// 后端API示例(Node.js/Express风格)
|
||
app.post('/api/v3/prompt-templates', authenticate, async (req, res) => {
|
||
const userId = req.user.id;
|
||
|
||
// 验证用户是否有创建权限
|
||
const hasPermission = await checkUserPermission(
|
||
userId,
|
||
'prompt_template:create:write'
|
||
);
|
||
|
||
if (!hasPermission) {
|
||
return res.status(403).json({
|
||
code: 403,
|
||
msg: '权限不足:您没有创建提示词模板的权限'
|
||
});
|
||
}
|
||
|
||
// 执行创建逻辑...
|
||
});
|
||
```
|
||
|
||
## 6. 实施步骤
|
||
|
||
### 第一步:数据库准备
|
||
```sql
|
||
-- 确保用户角色关联表存在
|
||
-- user_roles: user_id <-> role_id
|
||
|
||
-- 确保角色权限关联表存在
|
||
-- role_permissions: role_id <-> permission_id (已存在)
|
||
|
||
-- 为现有用户分配角色和权限
|
||
```
|
||
|
||
### 第二步:后端API开发
|
||
1. 创建 `GET /api/v3/users/current/permissions` 接口
|
||
2. 在登录响应中包含权限列表
|
||
3. 在JWT payload中包含permissions字段(可选)
|
||
|
||
### 第三步:前端集成
|
||
1. 修改 `app/api/login/auth.server.ts`,在getUserSession中获取权限
|
||
2. 修改 `app/root.tsx` loader,将permissions传递给前端
|
||
3. 创建 `app/hooks/usePermission.ts`
|
||
4. 修改 `app/routes/prompts._index.tsx` 使用权限检查
|
||
|
||
### 第四步:测试验证
|
||
1. 测试不同角色的用户看到的功能是否正确
|
||
2. 测试后端API权限验证是否生效
|
||
3. 测试权限变更后是否需要重新登录
|
||
|
||
## 7. 注意事项
|
||
|
||
1. **安全性**:前端权限检查只是UI控制,不能替代后端验证
|
||
2. **性能**:权限列表应该缓存,避免每次请求都查询数据库
|
||
3. **同步性**:权限变更后,考虑强制用户重新登录或刷新token
|
||
4. **降级方案**:如果获取权限失败,应该有合理的降级策略(如只读模式)
|
||
5. **审计日志**:所有权限相关的操作应该记录审计日志
|