Supabase认证概述 #

一、认证系统架构 #

1.1 整体架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                        客户端应用                            │
│         Web / Mobile / Desktop / Server                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Supabase Auth                            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │  Email/Pass │ │    OAuth    │ │  Magic Link │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │   Phone     │ │   SAML SSO  │ │  Anonymous  │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    PostgreSQL                               │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │ auth.users  │ │auth.sessions│ │auth.identit │           │
│  │             │ │             │ │    ies      │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
└─────────────────────────────────────────────────────────────┘

1.2 认证方式 #

认证方式 说明 适用场景
Email/Password 邮箱密码登录 传统应用
OAuth 第三方登录 快速登录
Magic Link 无密码登录 安全便捷
Phone 手机号验证码 移动应用
SAML SSO 企业单点登录 企业应用
Anonymous 匿名登录 试用体验

1.3 认证流程 #

text
用户登录流程
├── 1. 用户提交凭证
├── 2. Auth服务验证
├── 3. 生成JWT令牌
├── 4. 返回会话信息
│   ├── access_token
│   ├── refresh_token
│   └── user信息
├── 5. 客户端存储会话
└── 6. 后续请求携带token

二、用户表结构 #

2.1 auth.users表 #

sql
-- Supabase内置用户表
SELECT * FROM auth.users;

-- 主要字段
┌─────────────────┬──────────────────────────────┐
│ 字段            │ 说明                         │
├─────────────────┼──────────────────────────────┤
│ id              │ UUID主键                     │
│ email           │ 邮箱地址                     │
│ encrypted_pass  │ 加密密码                     │
│ email_confirmed │ 邮箱验证时间                 │
│ created_at      │ 创建时间                     │
│ updated_at      │ 更新时间                     │
│ last_sign_in_at │ 最后登录时间                 │
│ raw_user_meta   │ 用户元数据(JSON)             │
│ is_anonymous    │ 是否匿名用户                 │
└─────────────────┴──────────────────────────────┘

2.2 auth.sessions表 #

sql
-- 会话表
SELECT * FROM auth.sessions;

-- 主要字段
┌─────────────────┬──────────────────────────────┐
│ 字段            │ 说明                         │
├─────────────────┼──────────────────────────────┤
│ id              │ UUID主键                     │
│ user_id         │ 用户ID                       │
│ access_token    │ 访问令牌                     │
│ refresh_token   │ 刷新令牌                     │
│ expires_at      │ 过期时间                     │
│ created_at      │ 创建时间                     │
└─────────────────┴──────────────────────────────┘

2.3 auth.identities表 #

sql
-- 身份表(OAuth等)
SELECT * FROM auth.identities;

-- 主要字段
┌─────────────────┬──────────────────────────────┐
│ 字段            │ 说明                         │
├─────────────────┼──────────────────────────────┤
│ id              │ 身份ID                       │
│ user_id         │ 用户ID                       │
│ provider        │ 提供商(github/google等)      │
│ identity_data   │ 身份数据(JSON)               │
└─────────────────┴──────────────────────────────┘

2.4 创建用户资料表 #

sql
-- 创建用户资料表
CREATE TABLE public.profiles (
    id UUID REFERENCES auth.users(id) PRIMARY KEY,
    updated_at TIMESTAMPTZ DEFAULT NOW(),
    username TEXT UNIQUE,
    full_name TEXT,
    avatar_url TEXT,
    website TEXT,
    bio TEXT,
    
    CONSTRAINT username_length CHECK (char_length(username) >= 3)
);

-- 自动创建用户资料的触发器
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO public.profiles (id, full_name, avatar_url)
    VALUES (
        NEW.id,
        NEW.raw_user_meta_data->>'full_name',
        NEW.raw_user_meta_data->>'avatar_url'
    );
    RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 创建触发器
CREATE TRIGGER on_auth_user_created
    AFTER INSERT ON auth.users
    FOR EACH ROW
    EXECUTE FUNCTION public.handle_new_user();

三、JWT令牌 #

3.1 JWT结构 #

text
JWT结构: Header.Payload.Signature

Header:
{
    "alg": "HS256",
    "typ": "JWT"
}

Payload:
{
    "iss": "https://xxx.supabase.co/auth/v1",
    "sub": "user-uuid",
    "aud": "authenticated",
    "exp": 1234567890,
    "iat": 1234567890,
    "email": "user@example.com",
    "role": "authenticated"
}

Signature:
HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    jwt-secret
)

3.2 令牌类型 #

text
令牌类型
├── Access Token
│   ├── 用于API请求认证
│   ├── 有效期短(默认1小时)
│   ├── 包含用户信息
│   └── 存储在内存/localStorage
│
└── Refresh Token
    ├── 用于刷新Access Token
    ├── 有效期长(默认7天)
    ├── 不包含敏感信息
    └── 存储在数据库和客户端

3.3 令牌刷新 #

typescript
// 自动刷新(默认开启)
const supabase = createClient(url, key, {
  auth: {
    autoRefreshToken: true,  // 自动刷新
    persistSession: true,    // 持久化会话
  }
})

// 手动刷新
const { data, error } = await supabase.auth.refreshSession()

// 或只刷新token
const { data, error } = await supabase.auth.refreshAccessToken()

四、会话管理 #

4.1 获取会话 #

typescript
// 获取当前会话
const { data: { session }, error } = await supabase.auth.getSession()

// 监听会话变化
const { data: { subscription } } = supabase.auth.onAuthStateChange(
  (event, session) => {
    console.log('Auth event:', event)
    console.log('Session:', session)
  }
)

// 取消监听
subscription.unsubscribe()

4.2 会话事件 #

typescript
supabase.auth.onAuthStateChange((event, session) => {
  switch (event) {
    case 'INITIAL_SESSION':
      // 初始会话加载
      break
    case 'SIGNED_IN':
      // 用户登录
      break
    case 'SIGNED_OUT':
      // 用户登出
      break
    case 'PASSWORD_RECOVERY':
      // 密码恢复
      break
    case 'TOKEN_REFRESHED':
      // Token刷新
      break
    case 'USER_UPDATED':
      // 用户信息更新
      break
  }
})

4.3 多设备管理 #

typescript
// 获取所有会话
const { data, error } = await supabase.auth.getSessions()

// 登出其他设备
const { error } = await supabase.auth.signOut({
  scope: 'others'
})

// 登出所有设备
const { error } = await supabase.auth.signOut({
  scope: 'global'
})

五、权限级别 #

5.1 用户角色 #

text
Supabase用户角色
├── anon
│   ├── 匿名用户
│   ├── 受RLS限制
│   └── 公开API Key
│
├── authenticated
│   ├── 已认证用户
│   ├── 受RLS限制
│   └── 需要登录
│
└── service_role
    ├── 服务角色
    ├── 绕过RLS
    └── 仅服务端使用

5.2 在SQL中获取用户信息 #

sql
-- 获取当前用户ID
SELECT auth.uid();

-- 获取当前用户邮箱
SELECT auth.jwt() ->> 'email';

-- 在RLS策略中使用
CREATE POLICY "Users can only see own data"
ON profiles FOR SELECT
USING (auth.uid() = id);

5.3 自定义JWT声明 #

sql
-- 添加自定义JWT声明
CREATE OR REPLACE FUNCTION auth.jwt()
RETURNS JSONB AS $$
DECLARE
    claims JSONB;
BEGIN
    SELECT 
        COALESCE(
            jsonb_build_object(
                'user_role', role,
                'user_plan', plan
            ),
            '{}'::jsonb
        )
    INTO claims
    FROM profiles
    WHERE id = auth.uid();
    
    RETURN claims;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

六、安全最佳实践 #

6.1 密码安全 #

text
密码安全建议
├── 最小长度8位
├── 包含大小写字母
├── 包含数字和特殊字符
├── 不使用常见密码
├── 定期更换密码
└── 启用多因素认证

6.2 Token安全 #

typescript
// 安全存储配置
const supabase = createClient(url, key, {
  auth: {
    storage: {
      getItem: (key) => {
        // 使用安全存储
        return secureStorage.getItem(key)
      },
      setItem: (key, value) => {
        secureStorage.setItem(key, value)
      },
      removeItem: (key) => {
        secureStorage.removeItem(key)
      },
    },
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: true,
  },
})

6.3 HTTPS要求 #

text
安全要求
├── 生产环境必须使用HTTPS
├── Cookie设置Secure标志
├── 防止中间人攻击
└── 保护Token传输

七、认证配置 #

7.1 Dashboard配置 #

text
Dashboard > Authentication > Providers

可配置项
├── Email
│   ├── 启用/禁用
│   ├── 邮箱验证要求
│   └── 密码策略
│
├── OAuth Providers
│   ├── GitHub
│   ├── Google
│   ├── Facebook
│   ├── Twitter
│   └── 更多...
│
└── 其他设置
    ├── Magic Link
    ├── Phone Auth
    └── Anonymous Auth

7.2 URL配置 #

text
Dashboard > Authentication > URL Configuration

配置项
├── Site URL
│   └── 应用主URL
│
├── Redirect URLs
│   └── 允许的回调URL列表
│
└── Email Settings
    ├── SMTP配置
    └── 邮件模板

八、常见问题 #

8.1 会话丢失 #

typescript
// 检查会话状态
const { data: { session } } = await supabase.auth.getSession()

if (!session) {
  // 会话丢失,需要重新登录
  await supabase.auth.signInWithPassword({
    email: 'user@example.com',
    password: 'password'
  })
}

8.2 Token过期 #

typescript
// 自动刷新处理
supabase.auth.onAuthStateChange((event) => {
  if (event === 'TOKEN_REFRESHED') {
    console.log('Token refreshed successfully')
  }
})

// 手动刷新
try {
  await supabase.auth.refreshSession()
} catch (error) {
  // 刷新失败,需要重新登录
  await supabase.auth.signInWithPassword(...)
}

九、总结 #

认证系统要点:

概念 说明
用户表 auth.users
会话 JWT + Refresh Token
角色 anon, authenticated, service_role
存储 localStorage / 安全存储
刷新 自动刷新 / 手动刷新

下一步,让我们学习邮箱密码认证!

最后更新:2026-03-28