自定义声明和基于角色的访问控制 (RBAC)
自定义声明是附加到用户的特殊属性,你可以用它来控制用户访问应用的某些部分。例如:
🌐 Custom Claims are special attributes attached to a user that you can use to control access to portions of your application. For example:
{ "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"]}要用 custom claims 实现基于角色的访问控制 (RBAC),可以使用 自定义访问令牌认证钩子。这个钩子在发放令牌之前运行,你可以用它给用户的 JWT 添加额外的声明。
🌐 To implement Role-Based Access Control (RBAC) with custom claims, use a Custom Access Token Auth Hook. This hook runs before a token is issued. You can use it to add additional claims to the user's JWT.
本指南使用Slack 克隆示例来演示如何添加一个user_role声明并在你的行级安全 (RLS) 策略中使用它。
🌐 This guide uses the Slack Clone example to demonstrate how to add a user_role claim and use it in your Row Level Security (RLS) policies.
创建一个表格来跟踪用户角色和权限 #
🌐 Create a table to track user roles and permissions
在这个例子中,你将实现两个具有特定权限的用户角色:
🌐 In this example, you will implement two user roles with specific permissions:
moderator:版主可以删除所有消息,但不能删除通道。admin:管理员可以删除所有消息和通道。
-- Custom typescreate type public.app_permission as enum ('channels.delete', 'messages.delete');create type public.app_role as enum ('admin', 'moderator');-- USER ROLEScreate 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 'Application roles for each user.';-- ROLE PERMISSIONScreate 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 'Application permissions for each role.';🌐 For the full schema, see the example application on GitHub.
你现在可以在 SQL 中管理你的角色和权限。例如,要添加上述提到的角色和权限,运行:
🌐 You can now manage your roles and permissions in SQL. For example, to add the mentioned roles and permissions from above, run:
insert into public.role_permissions (role, permission)values ('admin', 'channels.delete'), ('admin', 'messages.delete'), ('moderator', 'messages.delete');创建认证钩子以应用用户角色 #
🌐 Create Auth Hook to apply user role
自定义访问令牌认证钩子 会在令牌发放之前运行。你可以用它来编辑 JWT。
🌐 The Custom Access Token Auth Hook runs before a token is issued. You can use it to edit the JWT.
-- Create the auth hook functioncreate or replace function public.custom_access_token_hook(event jsonb)returns jsonblanguage plpgsqlstableas $$ declare claims jsonb; user_role public.app_role; begin -- Fetch the user role in the user_roles table 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 -- Set the claim claims := jsonb_set(claims, '{user_role}', to_jsonb(user_role)); else claims := jsonb_set(claims, '{user_role}', 'null'); end if; -- Update the 'claims' object in the original event event := jsonb_set(event, '{claims}', claims); -- Return the modified or original event 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 "Allow auth admin to read user roles" ON public.user_rolesas permissive for selectto supabase_auth_adminusing (true);启用钩子 #
🌐 Enable the hook
在仪表板中,导航到 Authentication > Hooks (Beta),然后从下拉菜单中选择合适的 Postgres 函数。
🌐 In the dashboard, navigate to Authentication > Hooks (Beta) and select the appropriate Postgres function from the dropdown menu.
在本地开发时,请按照本地开发的说明操作。
🌐 When developing locally, follow the local development instructions.
想了解更多关于认证钩子的内容,请查看 认证钩子文档。
🌐 To learn more about Auth Hooks, see the Auth Hooks docs.
在 RLS 策略中访问自定义声明 #
🌐 Accessing custom claims in RLS policies
要在行级安全(RLS)策略中使用基于角色的访问控制(RBAC),创建一个 authorize 方法,从用户的 JWT 中读取其角色并检查该角色的权限:
🌐 To use Role-Based Access Control (RBAC) in Row Level Security (RLS) policies, create an authorize method that reads the user's role from their JWT and checks the role's permissions:
create or replace function public.authorize( requested_permission app_permission)returns boolean as $$declare bind_permissions int; user_role public.app_role;begin -- Fetch user role once and store it to reduce number of calls 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 策略中使用 authorize 方法。例如,要启用所需的删除权限,你可以添加以下策略:
🌐 You can then use the authorize method within your RLS policies. For example, to enable the desired delete access, you would add the following policies:
create policy "Allow authorized delete access" on public.channels for delete to authenticated using ( (SELECT authorize('channels.delete')) );create policy "Allow authorized delete access" on public.messages for delete to authenticated using ( (SELECT authorize('messages.delete')) );在你的应用中访问自定义声明 #
🌐 Accessing custom claims in your application
认证钩子只会修改访问令牌 JWT,而不会修改认证响应。因此,要在你的应用中访问自定义声明,例如你的浏览器客户端或服务器端中间件,你需要在认证会话中解码 access_token JWT。
🌐 The auth hook will only modify the access token JWT but not the auth response. Therefore, to access the custom claims in your application, e.g. your browser client, or server-side middleware, you will need to decode the access_token JWT on the auth session.
在 JavaScript 客户端应用中,你可以比如使用 jwt-decode 包:
🌐 In a JavaScript client application you can for example use the jwt-decode package:
import { 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 这样的包等。
🌐 For server-side logic you can use packages like express-jwt, koa-jwt, PyJWT, dart_jsonwebtoken, Microsoft.AspNetCore.Authentication.JwtBearer, etc.
结论 #
🌐 Conclusion
你现在有了一个强大的系统来管理数据库中的用户角色和权限,而且这些权限会自动同步到 Supabase Auth。
🌐 You now have a robust system in place to manage user roles and permissions within your database that automatically propagates to Supabase Auth.
更多资源 #
🌐 More resources