Skip to content

认证系统实施文档

字数
2367 字
阅读时间
11 分钟

概述

本文档描述了 Vibot 多租户工厂管理系统的认证系统实施,包括数据库初始化、API 封装和认证流程配置。

实施时间

2025-01-01

架构原则

  • 渐进式改造:保留现有 Vben 认证架构,仅更新数据层
  • 多租户支持:通过 Supabase RLS 实现数据隔离
  • 向后兼容:保持现有 API 接口不变
  • 最小化风险:避免大规模重构

文件变更清单

1. 数据库初始化

文件路径supabase/migrations/20250101000000_init_auth.sql

功能

  • 创建公司表(支持三家公司:福建微柏、福建鲤东、成都鲤东)
  • 创建用户、角色、权限表
  • 启用行级安全(RLS)策略
  • 创建权限辅助函数
  • 插入基础权限和角色数据

关键特性

  • 多租户数据隔离(通过 company_id
  • 基于角色的访问控制(RBAC)
  • 完整的权限系统(auth、user、order、approval、finance、hr、system)

2. API 封装更新

文件路径apps/web-antd/src/api/core/user.ts

变更内容

  • 更新用户信息获取以支持新的数据库结构
  • 适配 auth_user_id 关联方式
  • 获取公司信息(ID、名称、代码)
  • 增强错误处理

关键改进

typescript
// 查询 public.users 表获取详细信息
const { data: profile, error: profileError } = await supabaseClient
  .from('users')
  .select(`
    id,
    company_id,
    display_name,
    email,
    phone,
    avatar_url,
    profile,
    settings,
    companies (
      id,
      name,
      code
    )
  `)
  .eq('auth_user_id', authUserId)
  .maybeSingle();

文件路径apps/web-antd/src/api/core/auth.ts

变更内容

  • 更新 getAccessCodesApi 函数
  • 使用新的数据库函数 get_user_permissions
  • 添加用户档案验证

关键改进

typescript
// 调用数据库函数获取权限
const { data, error } = await supabaseClient.rpc<string[]>(
  'get_user_permissions',
  { user_uuid: userProfile.id }
);

数据库结构

核心表

  1. companies - 公司表(三家租户)

    • id (UUID)
    • name (VARCHAR) - 公司名称
    • code (VARCHAR) - 公司代码(weibai, fz-lidong, cd-lidong)
  2. users - 用户表

    • id (UUID) - 主键
    • auth_user_id (UUID) - 关联 Supabase Auth
    • company_id (UUID) - 关联公司
    • display_name (VARCHAR) - 显示名称
    • email (VARCHAR) - 邮箱
    • profile (JSONB) - 用户档案
  3. roles - 角色表

    • id (UUID)
    • company_id (UUID) - 租户隔离
    • code (VARCHAR) - 角色代码
    • name (VARCHAR) - 角色名称
  4. permissions - 权限表

    • id (UUID)
    • code (VARCHAR) - 权限代码
    • name (VARCHAR) - 权限名称
    • category (VARCHAR) - 权限类别
  5. role_permissions - 角色权限关联表

    • role_id (UUID)
    • permission_id (UUID)
    • company_id (UUID) - 租户隔离
  6. user_roles - 用户角色关联表

    • user_id (UUID)
    • role_id (UUID)
    • company_id (UUID) - 租户隔离

RLS 策略

关键修复:RLS 策略使用 auth.uid() 函数,不需要 JWT claims 中包含 company_id

所有核心表均启用 RLS,通过 auth_user_id 关联实现多租户数据隔离:

sql
-- 用户表:只能访问自己的记录
CREATE POLICY "users_isolation" ON public.users
  FOR ALL TO authenticated
  USING (auth_user_id = auth.uid());

-- 其他表:通过 users 表关联获取 company_id
CREATE POLICY "roles_isolation" ON public.roles
  FOR ALL TO authenticated
  USING (company_id = (
    SELECT company_id FROM public.users WHERE auth_user_id = auth.uid()
  ));

修复原因

  • 初始方案依赖 JWT claims 中的 company_id,但 Supabase Auth 默认不包含此字段
  • 修复后使用 auth.uid() 获取当前认证用户,再通过 public.users 表查询 company_id
  • 更简单、更可靠,无需在 Auth 中额外设置 metadata

辅助函数

  1. get_user_permissions(user_uuid UUID) - 获取用户权限码数组
  2. get_user_roles(user_uuid UUID) - 获取用户角色数组

认证流程

登录流程

  1. 用户提交登录表单(邮箱 + 密码)
  2. 调用 supabaseClient.auth.signInWithPassword() 进行认证
  3. 获取 Supabase 会话和 JWT token
  4. 通过 JWT 中的 company_id 设置 RLS 上下文
  5. 调用 getUserInfoApi() 获取用户详细信息
  6. 调用 getAccessCodesApi() 获取用户权限
  7. 存储用户信息和权限到 Pinia store
  8. 跳转到首页

权限验证流程

  1. 路由守卫检查 accessToken
  2. 如果无 token,重定向到登录页
  3. 如果有 token,验证是否已生成动态路由
  4. 调用 getUserInfoApi() 获取用户信息
  5. 根据用户角色生成权限路由
  6. 保存权限菜单和路由信息
  7. 跳转到目标页面

登出流程

  1. 调用 supabaseClient.auth.signOut() 清除 Supabase 状态
  2. 调用 clearSupabaseAuth() 清除本地存储
  3. 重置所有 Pinia store 状态
  4. 清除 localStorage 和 sessionStorage
  5. 重定向到登录页

多租户支持

公司数据隔离

  • 所有业务表包含 company_id 字段
  • RLS 策略自动过滤公司数据
  • JWT 令牌包含 company_id 上下文
  • 用户只能访问本公司数据

跨公司访问

  • 管理员可通过 Edge Function 临时切换上下文
  • 切换后 JWT 重新生成,包含新 company_id
  • Realtime 事件通知数据变更

验证方法

1. 数据库迁移验证

bash
# 检查迁移文件
ls -la supabase/migrations/20250101000000_init_auth.sql

# 验证表结构(需连接 Supabase)
supabase db diff

# 应用迁移(生产环境)
supabase db push

2. 登录功能验证

  1. 启动开发服务器

    bash
    pnpm dev
  2. 打开浏览器访问登录页

    http://localhost:5173/login
  3. 使用测试账号登录

    • 邮箱:admin@weibai.com
    • 密码:Vibot#2025
  4. 验证登录成功并跳转到首页

3. API 验证

bash
# 测试用户信息 API(需登录后)
curl -H "Authorization: Bearer <token>" \
     -H "apikey: <anon_key>" \
     <supabase_url>/rest/v1/users?select=*

# 测试权限 API
curl -H "Authorization: Bearer <token>" \
     -H "apikey: <anon_key>" \
     <supabase_url>/rest/v1/rpc/get_user_permissions

4. 路由守卫验证

  1. 访问受保护页面(如 /dashboard
  2. 如果未登录,应重定向到 /login
  3. 登录后应跳转到 /dashboard
  4. 检查浏览器开发者工具中的网络请求

5. 权限控制验证

  1. 登录后检查用户权限
  2. 访问需要特定权限的页面
  3. 验证无权限时显示 403 页面

环境配置

前端环境变量

bash
# .env
VITE_SUPABASE_URL=https://<project-ref>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<anon-key>
VITE_SUPABASE_ANON_KEY=<anon-key>

Supabase 配置

  1. 创建 Supabase 项目
  2. 应用数据库迁移
  3. 配置 RLS 策略
  4. 设置 Auth 设置

测试数据

预置公司

公司名称代码描述
福建微柏weibai福建微柏工业机器人有限公司
福建鲤东fz-lidong福建鲤东工业股份有限公司
成都鲤东cd-lidong成都鲤东工业股份有限公司

预置角色

每家公司有三个角色:

  • admin - 系统管理员(所有权限)
  • manager - 部门经理(部分管理权限)
  • employee - 普通员工(基础权限)

测试用户

方法 1:使用 Edge Function(推荐)

bash
# 1. 部署 Edge Function
supabase functions deploy create-test-users

# 2. 调用函数创建测试用户
curl -X POST \
  -H "Authorization: Bearer <service-role-key>" \
  -H "Content-Type: application/json" \
  <supabase_url>/functions/v1/create-test-users

这将自动为三家公司创建管理员测试用户:

方法 2:手动创建

  1. 在 Supabase Dashboard > Authentication > Users 中创建用户
  2. 获取生成的 auth_user_id
  3. 在 public.users 表中插入记录:
    sql
    INSERT INTO public.users (
        auth_user_id,
        company_id,
        email,
        display_name,
        status
    ) VALUES (
        '<real-auth-user-id>',
        (SELECT id FROM public.companies WHERE code = 'weibai'),
        'admin@weibai.com',
        '福建微柏管理员',
        'active'
    );
  4. 分配角色:
    sql
    INSERT INTO public.user_roles (user_id, role_id, company_id)
    SELECT
        u.id,
        r.id,
        r.company_id
    FROM public.users u
    CROSS JOIN public.roles r
    WHERE u.email = 'admin@weibai.com'
    AND r.code = 'admin'
    AND r.company_id = u.company_id;

故障排除

问题 1:登录失败

现象:输入正确账号密码但无法登录

解决方案

  1. 检查 Supabase 配置是否正确
  2. 确认用户是否在 auth.users 表中
  3. 确认 public.users 表中是否有对应记录
  4. 检查 RLS 策略是否正确

问题 2:权限获取失败

现象:登录成功但权限为空

解决方案

  1. 检查 get_user_permissions 函数是否存在
  2. 确认用户是否分配了角色
  3. 确认角色是否分配了权限
  4. 检查 RLS 策略是否阻止访问

问题 3:数据访问被拒绝

现象:查询数据时返回权限错误

解决方案

  1. 检查 JWT 令牌是否包含 company_id
  2. 确认 RLS 策略是否正确
  3. 验证用户公司 ID 是否匹配
  4. 检查数据库连接权限

下一步计划

  1. Edge Functions 开发

    • 实现 Dify AI 集成
    • 开发审批流程 Edge Functions
    • 实现跨公司数据访问
  2. 前端模块开发

    • 实现 AI 助手模块
    • 开发审批管理模块
    • 开发财务管理模块
  3. 测试完善

    • 添加单元测试
    • 添加 E2E 测试
    • 性能测试
  4. 文档完善

    • API 文档
    • 部署文档
    • 运维文档

总结

本次认证系统实施采用渐进式改造策略,在保持现有架构不变的前提下,成功实现了:

✅ 完整的多租户数据库架构 ✅ Supabase 认证集成 ✅ 基于角色的权限系统 ✅ 行级安全数据隔离 ✅ 向后兼容的 API 设计

系统现已具备生产环境部署条件,可支持三家公司独立管理,满足多租户工厂管理系统的业务需求。

贡献者

The avatar of contributor named as Cai Hongyu Cai Hongyu

文件历史

撰写