自定义声明和基于角色的访问控制 (RBAC)
自定义声明是附加到用户的特殊属性,可用于控制应用程序的某些部分的访问权限。例如
1{2 "user_role": "admin",3 "plan": "TRIAL",4 "user_level": 100,5 "group_name": "Super Guild!",6 "joined_on": "2022-05-20T14:28:18.217Z",7 "group_manager": false,8 "items": ["toothpick", "string", "ring"]9}要使用 自定义声明 实现基于角色的访问控制 (RBAC),请使用 自定义访问令牌身份验证钩子。此钩子在颁发令牌之前运行。您可以使用它来向用户的 JWT 添加额外的声明。
本指南使用 Slack 克隆示例 来演示如何添加 user_role 声明并在您的 行级安全 (RLS) 策略 中使用它。
创建表以跟踪用户角色和权限#
在此示例中,您将实现具有特定权限的两个用户角色
moderator:版主可以删除所有消息,但不能删除频道。admin:管理员可以删除所有消息和频道。
1-- Custom types2create type public.app_permission as enum ('channels.delete', 'messages.delete');3create type public.app_role as enum ('admin', 'moderator');45-- USER ROLES6create table public.user_roles (7 id bigint generated by default as identity primary key,8 user_id uuid references auth.users on delete cascade not null,9 role app_role not null,10 unique (user_id, role)11);12comment on table public.user_roles is 'Application roles for each user.';1314-- ROLE PERMISSIONS15create table public.role_permissions (16 id bigint generated by default as identity primary key,17 role app_role not null,18 permission app_permission not null,19 unique (role, permission)20);21comment on table public.role_permissions is 'Application permissions for each role.';现在,您可以在 SQL 中管理您的角色和权限。例如,要添加上述提到的角色和权限,请运行
1insert into public.role_permissions (role, permission)2values3 ('admin', 'channels.delete'),4 ('admin', 'messages.delete'),5 ('moderator', 'messages.delete');创建身份验证钩子以应用用户角色#
在颁发令牌之前运行 自定义访问令牌身份验证钩子。您可以使用它来编辑 JWT。
1-- Create the auth hook function2create or replace function public.custom_access_token_hook(event jsonb)3returns jsonb4language plpgsql5stable6as $$7 declare8 claims jsonb;9 user_role public.app_role;10 begin11 -- Fetch the user role in the user_roles table12 select role into user_role from public.user_roles where user_id = (event->>'user_id')::uuid;1314 claims := event->'claims';1516 if user_role is not null then17 -- Set the claim18 claims := jsonb_set(claims, '{user_role}', to_jsonb(user_role));19 else20 claims := jsonb_set(claims, '{user_role}', 'null');21 end if;2223 -- Update the 'claims' object in the original event24 event := jsonb_set(event, '{claims}', claims);2526 -- Return the modified or original event27 return event;28 end;29$$;3031grant usage on schema public to supabase_auth_admin;3233grant execute34 on function public.custom_access_token_hook35 to supabase_auth_admin;3637revoke execute38 on function public.custom_access_token_hook39 from authenticated, anon, public;4041grant all42 on table public.user_roles43to supabase_auth_admin;4445revoke all46 on table public.user_roles47 from authenticated, anon, public;4849create policy "Allow auth admin to read user roles" ON public.user_roles50as permissive for select51to supabase_auth_admin52using (true);启用钩子#
在仪表板中,导航到 身份验证 > 钩子 (Beta) 并从下拉菜单中选择适当的 Postgres 函数。
在本地开发时,请遵循 本地开发 说明。
要了解有关身份验证钩子的更多信息,请参阅 身份验证钩子文档。
在 RLS 策略中访问自定义声明#
要在行级安全 (RLS) 策略中利用基于角色的访问控制 (RBAC),请创建一个 authorize 方法,该方法从用户的 JWT 中读取用户角色并检查角色的权限
1create or replace function public.authorize(2 requested_permission app_permission3)4returns boolean as $$5declare6 bind_permissions int;7 user_role public.app_role;8begin9 -- Fetch user role once and store it to reduce number of calls10 select (auth.jwt() ->> 'user_role')::public.app_role into user_role;1112 select count(*)13 into bind_permissions14 from public.role_permissions15 where role_permissions.permission = requested_permission16 and role_permissions.role = user_role;1718 return bind_permissions > 0;19end;20$$ language plpgsql stable security definer set search_path = '';您可以在 RLS 指南 中了解更多关于在 RLS 策略中使用函数的信息。
然后,您可以在 RLS 策略中使用 authorize 方法。例如,要启用所需的删除访问权限,您将添加以下策略
1create policy "Allow authorized delete access" on public.channels for delete to authenticated using ( (SELECT authorize('channels.delete')) );2create policy "Allow authorized delete access" on public.messages for delete to authenticated using ( (SELECT authorize('messages.delete')) );在您的应用程序中访问自定义声明#
身份验证钩子只会修改访问令牌 JWT,而不会修改身份验证响应。因此,要在您的应用程序中访问自定义声明,例如您的浏览器客户端或服务器端中间件,您需要在身份验证会话中解码 access_token JWT。
在 JavaScript 客户端应用程序中,例如可以使用 jwt-decode 包
1import { jwtDecode } from 'jwt-decode'23const { subscription: authListener } = supabase.auth.onAuthStateChange(async (event, session) => {4 if (session) {5 const jwt = jwtDecode(session.access_token)6 const userRole = jwt.user_role7 }8})对于服务器端逻辑,您可以使用像 express-jwt、koa-jwt、PyJWT、dart_jsonwebtoken、Microsoft.AspNetCore.Authentication.JwtBearer 等包。
结论#
现在,您已经建立了一个强大的系统来管理数据库中的用户角色和权限,该系统会自动传播到 Supabase 身份验证。