Strapi 社区版用户权限控制与数据过滤完整指南
在 Strapi 社区版(Community Edition)中,没有企业版(Enterprise Edition)的「高级设置」界面(如后台直接通过 UI 设置条件过滤)。但你可以通过 自定义代码 实现相同的效果。以下是针对 Strapi v5 的完整解决方案:
1. 准备工作:确保模型关联正确
假设你有一个内容类型 Article
(文章),每个文章需要关联到创建它的用户(作者)。
1.1 在 Article
模型中添加 author
字段
- 类型:Relation(关系)
- 关联模型:User (来自
users-permissions
插件) - 关系类型:Many-to-One(一篇文章属于一个用户)
2. 自动填充作者信息
在创建文章时,自动将当前用户的 ID 关联到 author
字段。通过 生命周期钩子(Lifecycle Hooks) 实现:
// src/api/article/content-types/article/lifecycles.js
module.exports = {
async beforeCreate(event) {
const { data } = event.params;
const { id: userId } = event.state.user; // 获取当前登录用户 ID
data.author = userId; // 自动关联作者
},
};
3. 限制用户只能操作自己的文章
由于社区版没有条件过滤的 UI,需要通过以下两种方式实现权限控制:
方法 1:覆盖控制器方法(直接修改逻辑)
覆盖 Article
控制器的 find
、update
、delete
方法,添加权限检查。
// src/api/article/controllers/article.js
'use strict';
module.exports = {
// 查询列表时只返回当前用户的文章
async find(ctx) {
const { id: userId } = ctx.state.user;
ctx.query.filters = {
...ctx.query.filters,
author: { id: userId },
};
return super.find(ctx);
},
// 更新时检查文章是否属于当前用户
async update(ctx) {
const { id } = ctx.params;
const { id: userId } = ctx.state.user;
// 查询文章作者
const article = await strapi.entityService.findOne('api::article.article', id, {
populate: { author: true },
});
if (!article || article.author.id !== userId) {
return ctx.unauthorized('无权修改此文章');
}
return super.update(ctx);
},
// 删除时同样检查权限
async delete(ctx) {
const { id } = ctx.params;
const { id: userId } = ctx.state.user;
const article = await strapi.entityService.findOne('api::article.article', id, {
populate: { author: true },
});
if (!article || article.author.id !== userId) {
return ctx.unauthorized('无权删除此文章');
}
return super.delete(ctx);
},
};
方法 2:使用策略(Policies)统一控制
通过策略(Policy)在请求到达控制器前进行权限校验。
2.1 创建策略文件
// src/policies/is-article-owner.js
module.exports = async (policyContext, config, { strapi }) => {
const { id } = policyContext.params;
const { id: userId } = policyContext.state.user;
// 查询文章作者
const article = await strapi.entityService.findOne('api::article.article', id, {
populate: { author: true },
});
return article?.author?.id === userId;
};
2.2 将策略应用到路由
修改 Article
的路由配置:
// src/api/article/routes/article.js
module.exports = {
routes: [
{
method: 'GET',
path: '/articles',
handler: 'article.find',
config: {
policies: [
(ctx) => {
// 强制附加用户过滤条件
ctx.query.filters = { ...ctx.query.filters, author: { id: ctx.state.user.id } };
return true;
},
],
},
},
{
method: 'PUT',
path: '/articles/:id',
handler: 'article.update',
config: {
policies: ['global::is-article-owner'],
},
},
{
method: 'DELETE',
path: '/articles/:id',
handler: 'article.delete',
config: {
policies: ['global::is-article-owner'],
},
},
],
};
4. 测试权限控制
- 创建文章:
- 用户 A 登录后创建文章,
author
字段自动填充。
- 用户 A 登录后创建文章,
- 查询文章列表:
- 用户 A 只能看到自己创建的文章。
- 修改/删除他人文章:
- 用户 B 尝试操作用户 A 的文章,返回
403 Forbidden
。
- 用户 B 尝试操作用户 A 的文章,返回
5. 补充:公共接口的权限配置
如果部分接口需要公开访问(如 findOne
),可以在后台设置角色权限:
- 进入 Settings → Users & Permissions → Roles。
- 编辑 Public 角色,为
Article
分配findOne
权限(无需登录即可查看单篇文章)。 - 编辑 Authenticated 角色,分配
find
、update
、delete
权限。
总结
- 社区版限制:没有企业版的图形化条件过滤,但完全可以通过代码实现相同功能。
- 核心逻辑:
- 关联用户:通过
author
字段绑定文章和用户。 - 自动填充:使用生命周期钩子在创建时自动关联作者。
- 权限控制:覆盖控制器方法或使用策略,强制过滤用户数据。
- 关联用户:通过
- 扩展性:此方案无需修改 Strapi 核心代码,兼容后续版本升级。