6.6 KiB
6.6 KiB
11 角色、权限与路由整合策略
本节提供一套完整的“角色定义 → RLS 策略 → 前端路由/跳转 → 业务流程”整合方案,旨在将数据库安全模型与项目实际开发无缝结合。
1. 角色定义(权威口径)
为避免权限判断分裂,项目应统一使用 public.ak_users.role 作为唯一权威的角色字段。
1.1 推荐的角色枚举
customer:消费者merchant:商家delivery:配送员service:客服admin:平台管理员analytics:数据分析/运营角色
决策点:
analytics角色是可选的。如果运营/分析师与admin权限边界不清,可以先统一为admin。- 但长远看,为“数据查看者”设定独立角色有利于最小权限原则。
1.2 如何在数据库中获取当前用户角色
通常通过一个函数实现,该函数内部使用 auth.uid()。
CREATE OR REPLACE FUNCTION public.get_current_user_role()
RETURNS TEXT
LANGUAGE sql
SECURITY DEFINER
AS $$
SELECT role FROM public.ak_users WHERE auth_id = auth.uid() LIMIT 1;
$$;
2. RLS 策略与权限设计
2.1 权限分层(推荐)
-
A. Row Owner(行归属者)
- 用户只能访问自己的数据,如地址、购物车、收藏、个人订单。
- RLS 策略核心:
auth.uid() = (SELECT auth_id FROM ak_users WHERE id = <row.user_id>)
-
B. Business Owner(业务归属者)
- 商家只能访问自己店铺的数据,如商品、店铺订单。
- RLS 策略核心:
auth.uid() = (SELECT auth_id FROM ak_users WHERE id = <row.merchant_id>)
-
C. Privileged(特权角色)
admin/analytics角色需要访问全局数据,尤其是聚合统计。- 强烈建议:不要为这些角色直接开放表的全局
SELECT权限。
2.2 如何让 admin/analytics 安全地看全局数据?
推荐方案:RPC + SECURITY DEFINER
- 维持表的严格 RLS:确保
customer/merchant无法越权。 - Analytics 页面只调用 RPC:例如
rpc_analytics_*系列函数。 - RPC 函数必须
SECURITY DEFINER:使其以“函数所有者”(通常是postgres超级用户)的权限执行,从而绕过调用者的 RLS 限制。 - RPC 函数内部必须做显式鉴权:这是安全闭环的关键。
RPC 鉴权模板:
CREATE OR REPLACE FUNCTION public.rpc_analytics_sales_kpis(
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (...)
LANGUAGE plpgsql
SECURITY DEFINER -- 以函数所有者权限执行
SET search_path = public -- 显式设置 search_path,避免 search_path 攻击
AS $$
BEGIN
-- 1. 在函数入口处做权限检查
IF get_current_user_role() NOT IN ('admin', 'analytics') THEN
RAISE EXCEPTION 'Permission denied: required role admin or analytics';
END IF;
-- 2. 执行统计(因为是 SECURITY DEFINER,这里可以查到所有数据)
RETURN QUERY
WITH ...
-- ... 统计逻辑 ...
END;
$$;
现状风险:当前
rpc_analytics_*脚本未包含SECURITY DEFINER与内部鉴权。若直接部署,当 RLS 开启时,admin/analytics调用会因权限不足而查不到数据。
3. 前端项目整合:路由守卫与业务流程
3.1 路由分组(按角色)
项目页面按角色划分,便于集中管理路由与权限。
/pages/mall/consumer/**/pages/mall/merchant/**/pages/mall/delivery/**/pages/mall/admin/**/pages/mall/analytics/**
3.2 路由守卫(客户端鉴权)
在 services/analytics/authGuard.uts(或类似文件)中,应提供更精细的守卫函数。
守卫函数建议:
// services/auth/guard.uts (示例)
import { getCurrentUser } from './user.uts' // 假设此函数能获取当前登录用户及其角色
// 1. 确保已登录
export function ensureLoggedIn(options: { redirect?: string } = {}): boolean {
const user = getCurrentUser();
if (!user) {
uni.navigateTo({ url: options.redirect ?? '/pages/user/login' });
return false;
}
return true;
}
// 2. 确保具备指定角色之一
export function ensureRole(allowedRoles: Array<string>, options: { toastTitle?: string } = {}): boolean {
if (!ensureLoggedIn()) return false;
const user = getCurrentUser();
if (!user || !allowedRoles.includes(user.role)) {
uni.showToast({ title: options.toastTitle ?? '无权访问', icon: 'none' });
// 可选择返回上一页或跳转首页
setTimeout(() => uni.navigateBack(), 1500);
return false;
}
return true;
}
在 Analytics 页面中使用:
// pages/mall/analytics/index.uvue
onLoad(() => {
if (!ensureRole(['admin', 'analytics'], { toastTitle: '仅管理员可访问数据分析' })) {
return;
}
initDashboard();
});
3.3 业务流程闭环(以 Analytics 首页为例)
- 用户访问
/pages/mall/analytics/index。 - 前端守卫:
onLoad中ensureRole(['admin', 'analytics'])执行:- 未登录 → 跳转登录页
- 已登录但角色不符 → toast 提示 + 返回
- 调用 Service:
dashboardService.uts的fetch...函数被调用。 - 执行 RPC:
rpcOrNull('rpc_analytics_sales_kpis', ...)发起请求。 - 数据库鉴权:
rpc_analytics_sales_kpis函数内部首先检查get_current_user_role()是否为admin/analytics。- 权限不足 →
RAISE EXCEPTION,前端收到错误。 - 权限通过 → 执行统计。
- 权限不足 →
- 数据返回:前端拿到聚合数据并渲染。
这个流程实现了“前端快速失败 + 后端强制校验”的安全闭环。
4. 权限矩阵(总结)
| 角色 | customer |
merchant |
admin/analytics |
|---|---|---|---|
| 可读 | 上架商品、自己的(订单/地址/购物车/收藏/券) | 自己的(商品/订单/店铺数据) | 全局聚合数据(通过 RPC) |
| 可写 | 自己的(地址/购物车/收藏/订单创建) | 自己的(商品/发货/售后) | 通常不直接写业务表(通过后台管理功能) |
5. 待办与实现建议
- 统一角色字段:在项目中明确
ak_users.role为唯一权威,并提供获取当前用户角色的函数。 - 增强 RPC 安全性:为所有
rpc_analytics_*函数增加SECURITY DEFINER与内部权限检查。 - 实现前端路由守卫:创建
ensureRole函数,并在所有analytics子包页面中统一调用。