通过Sequelize关联返回数据时如何保护用户敏感字段
解决Feathers+Sequelize关联查询时隐藏敏感字段的问题
方法1:自定义After钩子过滤关联字段
hooks-common的protect对嵌套数组的支持有时候不太灵光,直接写个after钩子手动过滤最靠谱,还不影响用户服务的认证逻辑。
在groups服务的hooks文件里加这段代码:
// src/services/groups/groups.hooks.js module.exports = { after: { all: [ context => { // 处理单条或多条记录 const cleanUserFields = record => { if (record.users && Array.isArray(record.users)) { record.users = record.users.map(user => { // 剔除password和email,保留其他字段 const { password, email, ...safeUser } = user; return safeUser; }); } return record; }; if (Array.isArray(context.result)) { context.result = context.result.map(cleanUserFields); } else if (context.result) { context.result = cleanUserFields(context.result); } return context; } ] } };
这个钩子会在所有请求返回后执行,遍历每个组记录里的users数组,删掉敏感字段,完全不碰用户服务的默认逻辑,认证流程不受影响。
方法2:给关联设置单独的Scope
别修改users模型的defaultScope(改了会搞崩认证),而是在groups关联users的时候直接指定要查询的字段,从数据库层面就过滤掉敏感信息。
修改关联代码:
// 模型关联处 groups.belongsToMany(models.users, { through: 'group_users', // 只查询需要的字段,排除password和email scope: { attributes: ['id', 'username', 'nickname'] // 换成你需要保留的字段 } });
如果有特殊场景需要获取完整的用户字段(比如内部服务调用),可以在查询时覆盖这个scope:
// 比如在groups服务的某个自定义方法里 const fullGroup = await context.app.service('groups').get(groupId, { sequelize: { include: [{ model: context.app.service('users').Model, attributes: { exclude: [] } // 不排除任何字段 }] } });
方法3:正确使用hooks-common的protect(针对嵌套字段)
之前用protect没生效是因为路径写法不对,嵌套数组需要用users[].password这种格式,再结合iff控制只对外部请求生效,内部调用还能拿到完整字段。
代码示例:
// src/services/groups/groups.hooks.js const { protect, iff, isProvider } = require('feathers-hooks-common'); module.exports = { after: { all: [ // 只对客户端等外部请求隐藏敏感字段,内部调用不受影响 iff(isProvider('external'), protect('users[].password', 'users[].email')) ] } };
这三种方法都能解决问题,选哪种看你的场景:要灵活就用自定义钩子,要从数据库层面控制就用关联scope,要简洁就用改进后的protect。
内容的提问来源于stack exchange,提问作者David Robert Hoare
相关产品推荐
相关产品推荐

