uniCloud云数据库不自动隔离租户数据,必须在每次查询/写入时显式添加tenantId条件;云函数需通过用户ID查出归属租户ID并强制校验,禁止信任客户端传入的tenantId;云存储文件需手动拼接tenantId前缀避免冲突,并为tenantId字段建立数据库索引保障性能。

uniCloud云数据库如何按租户自动隔离数据
uniCloud本身不自动做租户级数据隔离,所有集合默认共享,必须靠开发者在查询/写入时显式带上租户标识(如tenantId),否则数据会混在一起。这不是配置开关能解决的问题,而是设计层面的硬约束。
常见错误是:只在登录后存tenantId到uni.getStorageSync('tenantId'),但后续所有db.collection().add()或.get()都忘了加where({ tenantId: xxx })——结果就是A租户能看到B租户的数据。
- 所有云函数入口必须校验并透传
tenantId:从客户端传入 → 云函数参数校验 → 写入/查询时强制作为条件 - 避免直接暴露集合操作:不要把
db.collection('user')返回给前端,所有读写必须走云函数封装 - 推荐用
uniCloud.callFunction而非uniCloud.database直连,便于统一拦截和注入租户上下文
云函数中如何安全提取并验证tenantId
不能信任客户端传来的任何tenantId,必须结合登录态二次确认。比如用户登录后,服务端已知该用户归属tenantId: 't-123',那么后续所有请求中的tenantId必须与该值一致,否则拒绝。
示例云函数getOrderList开头应这样校验:
exports.main = async function (event, context) {
const { userId } = event
const db = uniCloud.database()
// 先查用户归属租户
const userRes = await db.collection('user').where({ _id: userId }).field('tenantId').get()
if (!userRes.result.length) throw new Error('user not found')
const tenantId = userRes.result[0].tenantId
// 强制使用查出来的tenantId,忽略event.tenantId
const orderRes = await db.collection('order').where({ tenantId, status: 'paid' }).get()
return orderRes.result
}
注意:这里没用event.tenantId,而是从数据库查出用户绑定的tenantId,杜绝伪造。
如何避免不同租户间云存储文件名冲突
uniCloud云存储不支持目录级权限隔离,uploadFile的fileID全局唯一,但若两个租户都传avatar.png,后上传的会覆盖前一个——除非你主动加租户前缀。
- 上传时强制拼接路径:
tenantId + '/' + fileName,例如t-123/avatar.png - 不要依赖
fileID自动生成逻辑,必须自己构造可预测、带租户标识的fileID - 下载链接需带签名且限制
tenantId,防止URL被猜解后跨租户访问
调用uniCloud.uploadFile时,fileID字段必须手动设为t-123_avatar_abc123.png这类格式,不能留空。
多租户场景下云函数如何复用又不失安全性
写12个几乎一样的云函数(每个租户一个)不可维护。正确做法是:共用同一套云函数代码,靠运行时动态注入租户上下文。
关键点:
- 所有云函数统一接收
{ userId, ...otherParams },不接收tenantId - 内部通过
db.collection('user').doc(userId).field('tenantId')实时查租户归属 - 数据库索引必须包含
tenantId字段,否则where({ tenantId, xxx })查询会慢甚至超时
容易被忽略的是:每个集合都要为tenantId建单独索引,否则高并发下查询性能断崖下跌。在HBuilderX的云数据库控制台里,进集合 → 索引管理 → 添加字段tenantId的单字段索引。


















