自定义声明与基于角色的访问控制(RBAC)
自定义声明(Custom Claims)是附加到用户的特殊属性,可用于控制应用程序不同部分的访问权限。例如:
123456789{ "user_role": "admin", "plan": "TRIAL", "user_level": 100, "group_name": "Super Guild!", "joined_on": "2022-05-20T14:28:18.217Z", "group_manager": false, "items": ["toothpick", "string", "ring"]}
要实现基于自定义声明的角色访问控制(RBAC),请使用自定义访问令牌认证钩子。该钩子在令牌颁发前执行,可用于向用户的JWT添加额外声明。
本指南使用Slack克隆示例来演示如何添加user_role
声明,并将其用于行级安全(RLS)策略中。
创建用户角色和权限表
在本示例中,您将实现两种具有特定权限的用户角色:
moderator
(版主):可以删除所有消息但不能删除频道admin
(管理员):可以删除所有消息和频道
123456789101112131415161718192021-- 自定义类型create type public.app_permission as enum ('channels.delete', 'messages.delete');create type public.app_role as enum ('admin', 'moderator');-- 用户角色表create table public.user_roles ( id bigint generated by default as identity primary key, user_id uuid references auth.users on delete cascade not null, role app_role not null, unique (user_id, role));comment on table public.user_roles is '每个用户的应用程序角色';-- 角色权限表create table public.role_permissions ( id bigint generated by default as identity primary key, role app_role not null, permission app_permission not null, unique (role, permission));comment on table public.role_permissions is '每个角色的应用程序权限';
现在您可以通过SQL管理角色和权限。例如,要添加上述提到的角色和权限,请执行:
12345insert into public.role_permissions (role, permission)values ('admin', 'channels.delete'), ('admin', 'messages.delete'), ('moderator', 'messages.delete');
创建认证钩子以应用用户角色
自定义访问令牌认证钩子在令牌颁发前运行,可用于编辑JWT。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152-- 创建认证钩子函数create or replace function public.custom_access_token_hook(event jsonb)returns jsonblanguage plpgsqlstableas $$ declare claims jsonb; user_role public.app_role; begin -- 从user_roles表中获取用户角色 select role into user_role from public.user_roles where user_id = (event->>'user_id')::uuid; claims := event->'claims'; if user_role is not null then -- 设置声明 claims := jsonb_set(claims, '{user_role}', to_jsonb(user_role)); else claims := jsonb_set(claims, '{user_role}', 'null'); end if; -- 更新原始事件中的'claims'对象 event := jsonb_set(event, '{claims}', claims); -- 返回修改后或原始的事件 return event; end;$$;grant usage on schema public to supabase_auth_admin;grant execute on function public.custom_access_token_hook to supabase_auth_admin;revoke execute on function public.custom_access_token_hook from authenticated, anon, public;grant all on table public.user_rolesto supabase_auth_admin;revoke all on table public.user_roles from authenticated, anon, public;create policy "允许认证管理员读取用户角色" ON public.user_rolesas permissive for selectto supabase_auth_adminusing (true)
启用钩子
在控制面板中,导航至 认证 > 钩子(测试版)
并从下拉菜单中选择相应的Postgres函数。
在本地开发时,请遵循本地开发指南。
要了解更多关于认证钩子的信息,请参阅认证钩子文档。
在RLS策略中访问自定义声明
要在行级安全(RLS)策略中实现基于角色的访问控制(RBAC),可以创建一个authorize
方法,该方法从用户的JWT中读取角色并检查角色权限:
1234567891011121314151617181920create or replace function public.authorize( requested_permission app_permission)returns boolean as $$declare bind_permissions int; user_role public.app_role;begin -- 获取用户角色并存储以减少调用次数 select (auth.jwt() ->> 'user_role')::public.app_role into user_role; select count(*) into bind_permissions from public.role_permissions where role_permissions.permission = requested_permission and role_permissions.role = user_role; return bind_permissions > 0;end;$$ language plpgsql stable security definer set search_path = '';
您可以在RLS指南中了解更多关于在RLS策略中使用函数的信息。
然后您可以在RLS策略中使用authorize
方法。例如,要启用所需的删除权限,可以添加以下策略:
12create policy "允许授权删除访问" on public.channels for delete to authenticated using ( (SELECT authorize('channels.delete')) );create policy "允许授权删除访问" on public.messages for delete to authenticated using ( (SELECT authorize('messages.delete')) );
在应用中访问自定义声明
Auth Hook 只会修改访问令牌 JWT 而不会修改认证响应。因此,要在您的应用中访问自定义声明(例如浏览器客户端或服务器端中间件),您需要解码认证会话中的 access_token
JWT。
在 JavaScript 客户端应用中,您可以使用 jwt-decode
包:
12345678import { jwtDecode } from 'jwt-decode'const { subscription: authListener } = supabase.auth.onAuthStateChange(async (event, session) => { if (session) { const jwt = jwtDecode(session.access_token) const userRole = jwt.user_role }})
对于服务器端逻辑,您可以使用诸如 express-jwt
、koa-jwt
、PyJWT
、dart_jsonwebtoken、Microsoft.AspNetCore.Authentication.JwtBearer 等包。
总结
您现在拥有了一套完善的系统来管理数据库中的用户角色和权限,这些权限会自动同步到 Supabase Auth。