Sails Policies 权威指南:从 ACL 配置到授权中间件源码解析
后端【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址https://gitcode.com/gh_mirrors/sa/sails点击查看免费下载Policies策略是 Sails 实时 MVC 框架内置的授权与访问控制机制它允许你在 action 执行之前运行一段逻辑从而决定是否继续处理该请求最常见的用途是把某些 action 限制为仅登录用户可访问。本文将基于 Sails 官方文档与当前仓库源码lib/hooks/policies/index.js、lib/router/bind.js等系统讲解config/policies.js声明式 ACL 的完整语法控制器、独立 action、全局通配符、优先级并深入剖析 policy 的加载、绑定与执行链路最后给出从简单登录校验到动态权限Helper 方案的完整实战示例。OverviewPolicies 到底是什么在 Sails 中Policies 是授权与访问控制的通用工具它们在 action 运行之前执行某些逻辑以此判断是否继续处理该请求。Policies 最常见的应用场景是将某些 action 限制为仅登录用户logged-in users可访问。注意Policies只作用于控制器controllers和 action不作用于视图views。如果你在 routes.js 配置文件 中定义了一条直接指向某个视图的路由那么不会有任何 policy 应用到它。为确保 policy 生效你应该改为定义一个渲染该视图的 action然后把路由指向这个 action。从实现上看每个 policy 本质上是 Express 风格的中间件函数(req, res, next)在目标 action 之前按顺序执行。这一点在 config/policies.js 解剖文档 中有明确说明policy 文件如api/policies/isLoggedIn.js可以放入api/policies/目录之后即可通过其文件名去掉扩展名如isLoggedIn来引用。什么时候该用 PoliciesSails 官方建议避免在应用里实现过多或过于复杂的 policy。当实现细粒度、基于角色的权限时应依靠你的 actions 来拒绝未经授权的访问同时action 也应当负责对响应中发送的视图局部变量view locals和 JSON 响应数据做必要的个性化处理。例如当你需要实现用户级或角色级权限时最直接的做法是在控制器 action 的顶部处理相关校验——无论是内联编写还是调用一个 helper。遵循这一最佳实践将显著提升代码的可维护性。简单地说Policies 擅长的是二元的yes/no访问控制它非常适合检查用户是否已登录当前登录用户是否为超级管理员这类简单判断。而复杂权限取决于谁和想做什么更适合放到 action 或 helper 中处理详见下文动态权限一节。用 Policies 保护 action 与控制器Sails 内置了一个位于config/policies.js的ACL访问控制列表。该文件用于把 policy 映射到 action 和控制器上。这个文件是声明式declarative的它描述的是应用权限应该是什么样what而不是如何实现how。这让新开发者更容易理解当前配置也让应用在需求随时间变化时更加灵活。config/policies.js是一个字典dictionary其属性和值的形态取决于你是在为控制器还是为独立 action 配置 policy。将 Policies 应用于控制器要将 policy 应用于控制器请在config/policies.js字典中使用控制器名称作为属性名并把它的值设为另一个字典该字典将控制器中的 action 映射到要应用的 policy 上。使用*表示所有未映射的 action。policy 的名称与其文件名相同去掉扩展名module.exports.policies { UserController: { // 默认要求请求来自已登录用户 // 运行 api/policies/isLoggedIn.js 中的 policy *: isLoggedIn, // 只允许管理员用户删除其他用户 // 运行 api/policies/isAdmin.js 中的 policy delete: isAdmin, // 允许任何人访问 login action即使未登录 login: true } };将 Policies 应用于独立 action要为某个或多个独立 action 应用 policy请使用action 路径相对于api/controllers作为config/policies.js字典中的属性名并把值设为应应用于这些 action 的 policy 或 policy 数组。通过在 action 路径末尾使用通配符*你可以把 policy 应用于所有以该路径开头的 action。下面是与上面相同的策略集改写为应用于独立 action 的写法module.exports.policies { user/*: isLoggedIn, user/delete: isAdmin, user/login: true }注意这个例子与基于控制器的 policy 写法略有不同——isLoggedInpolicy 将应用于api/controllers/user文件夹及其子文件夹中的所有 action但user/delete和user/login除外原因见下一节。Policy 的排序与优先级Policies 不会级联do not cascade这一点非常重要。在上面的例子中isLoggedInpolicy 会应用于UserController.js文件中的所有 action或位于api/controllers/user下的独立 action但delete和login除外。如果希望给一个 action 应用多个 policy请用数组列出这些 policygetEncryptedData: [isLoggedIn, isInValidRegion]从源码角度来印证这种非级联、精确覆盖的机制在 lib/hooks/policies/index.js 的buildPolicyMap()中所有 policy 键会被按字母序排序使更具体的键如user/foo排在更宽泛的键如user/*之后随后对于*全局键会为排在它后面的每个目标追加!target形式的排除项对于/*通配符键也会对后续匹配到的目标追加!前缀从而保证更精确的映射覆盖更宽泛的映射——注释里明确写着for now policies are NOT cumulative目前 policies 不会累积。最终这些target,!exclude1,!exclude2形式的逗号分隔字符串会通过sails.registerActionMiddleware(policies, targets)注册见 bindPolicies。而真正匹配时lib/router/bind.js 会把每个注册键按,拆分成目标数组并排序!开头的排前面先检查否定目标!...是否匹配 action identity再检查肯定目标*、xxx/*通配、精确匹配从而决定该 policy 是否加入待执行中间件链。对 Blueprint Action 使用 PoliciesSails 内置的 blueprint API 是使用普通 Sails action 实现的唯一区别在于 blueprint action 是**隐式implicit**的。要对 blueprint action 应用 policy只需像上面的例子一样设置 policy 映射只不过把目标指向控制器中相应隐式 blueprint action 的名称或作为独立 action。例如module.exports.policies { UserController: { // 将 isLoggedIn policy 应用于 UserController 的 update action update: isLoggedIn } };或module.exports.policies { user/update: isLoggedIn };全局 Policies你可以通过*属性把某个 policy 应用到所有未被显式映射的 action。例如module.exports.policies { *: isLoggedIn, user/login: true };这会把isLoggedInpolicy 应用到除api/controllers/user/login.js中的loginaction或api/controllers/UserController.js中的loginaction之外的所有 action。内置 PoliciesSails 提供了两个内置 policy可以全局应用也可以应用于特定控制器或 actiontrue公共访问允许任何人访问被映射的 controller/actionfalse禁止访问不允许任何人访问被映射的 controller/action*: true是所有控制器和 action 的默认 policy。在生产环境中好的做法是将其改为false以防止访问任何你可能无意暴露的逻辑。这两个内置值在源码中有明确的对应实现lib/hooks/policies/index.js 定义了neverAllow调用res.forbidden()和alwaysAllow直接调用next()两个中间件函数并分别标记为POLICY: false (neverAllow)与POLICY: true (alwaysAllow)同时true/false会先被包装成单元素数组且如果数组中还有其他 policy 会直接抛错E_INVALID_POLICY_CONFIG也就是说true/false必须是该目标唯一的策略。此外配置校验非常严格buildPolicyMappolicy 可以是字符串必须对应已加载的 policy 名称否则抛出E_INVALID_POLICY_CONFIG、函数或true/false其他任何类型都会在应用启动时直接报错终止。编写你的第一个 Policy下面是一个简单的isLoggedInpolicy用于阻止未认证用户访问。它检查 session 中是否有userId属性如果找不到就发送默认的forbidden响应。对许多应用来说这可能就是唯一需要的 policy。下面的例子假设在认证用户的控制器 action 中你把req.session.userId设置成了一个真值truthy。// api/policies/isLoggedIn.js module.exports async function (req, res, proceed) { // 如果设置了 req.me我们就知道这个请求来自已登录用户 // 因此可以安全地继续执行下一个 policy—— // 或者如果这是最后一个 policy则继续执行相应 action。 // 关于 req.me 从哪来请查看本应用的自定义 hookapi/hooks/custom/index.js。 if (req.me) { return proceed(); } //--• // 否则这个请求不是来自已登录用户。 return res.forbidden(); };Policy 的加载机制从源码看policy 文件的加载发生在 policies hook 的loadMiddleware()中lib/hooks/policies/index.js它调用sails.modules.loadPolicies()后者lib/hooks/moduleloader/index.js使用includeAll.optional()从sails.config.paths.policies即api/policies/目录加载所有非md/txt的模块并扁平化、保留目录结构。加载后如果通过编程方式在sails.config.policies.moduleDefinitions中提供了 policy 函数会覆盖从磁盘加载的同名 policy每个加载的 policy 都会被校验必须是函数否则抛出E_INVALID_POLICY错误每个 policy 函数会被打上_middlewareType POLICY: name标记便于日志和调试。在路由上直接绑定 Policy除了config/policies.js的声明式映射policies hook 还监听了route:typeUnknown事件lib/hooks/policies/index.js允许你在config/routes.js中通过policy: ...路由选项把 policy手动绑定到显式路由上。若引用的 policy 不存在会调用 errors/fatal.js 中的__UnknownPolicy__打印错误Unknown policy, xxx, referenced in ...并终止进程。进阶动态权限与 Helper 方案生成一个带示例的 Web App要查看访问控制的实际示例——以及登录、认证和密码恢复——请生成一个 starter web appsails new foo # 然后选择 Web App动态权限结合数据库的细粒度控制对于更复杂的权限方案比如请求方主体的访问权同时取决于他们是谁who they are和他们想做什么what theyre trying to do你需要引入数据库。虽然你也可以用 policy 实现这一点但通常更直接、更易维护的做法是使用 helper。例如你可以创建api/helpers/check-permissions.jsmodule.exports { friendlyName: Check permissions, description: Look up a user\s rights within a particular organization., inputs: { userId: { type: number, required: true }, orgId: { type: number, required: true } }, exits: { success: { outputFriendlyName: Rights, outputDescription: A users rights within an org., outputType: [string] }, orgNotFound: { description: No such organization exists. } }, fn: async function(inputs, exits) { var org await Organization.findOne(inputs.orgId) .populate(adminUsers, { id: inputs.userId }) .populate(regularUsers, { id: inputs.userId }); if (!org) { throw orgNotFound; } var rights []; if (org.regularUsers.length ! 0) { rights [basicAccess, inviteRegularUsers]; } else if (org.adminUsers.length ! 0) { rights [basicAccess, inviteRegularUsers, removeRegularUsers, inviteOrgAdmins]; } else if (org.owner inputs.userId) { rights [basicAccess, inviteRegularUsers, removeRegularUsers, inviteOrgAdmins, removeOrDemoteOrgAdmins]; } // ^^这里可以按你的需要做到任意简单或精细例如 // [basicAccess, inviteRegularUsers, inviteOrgAdmins, removeRegularUsers, removeOrDemoteOrgAdmins] return exits.success(rights); } };你的 action——例如api/controllers/demote-org-admin.js——可能长这样//… var rights await checkPermissions(this.req.session.userId, inputs.orgId) .intercept(orgNotFound, notFound); if (!_.contains(rights, removeOrDemoteOrgAdmins)) { throw forbidden; } await Organization.removeFromCollection(inputs.orgId, adminUsers, inputs.targetUserId); await Organization.addToCollection(inputs.orgId, regularUsers, inputs.targetUserId); return exits.success();注意请记住虽然我们在这里使用了checkPermissions(…,…)但我们也可以使用.with()切换到命名参数await checkPermissions.with({ userId: this.req.session.userId, orgId: inputs.orgId });你可以在不同场景中选择不同的 helper 调用方式来增强代码可读性。拿不定主意时一个很好的最佳实践是先追求显式explicitness再追求可读性readability最后才追求简洁conciseness。当然当你更频繁地使用某个 helper 并逐渐熟悉它之后这些优先级可能会发生变化。这种policy 做二元放行 action/helper 做细粒度业务授权的组合正是 Sails 官方推荐的权限架构policy 保持简单复杂判断下沉到可复用、可测试的 helper 与 action 中。小结与最佳实践清单Policies 只保护 action/controller不保护直接指向视图的路由——需要保护视图时先把它包进 action。保持 policy 简单二元判断是否登录、是否管理员放 policy细粒度、角色化权限放 action 或 helper。Policies 不级联更具体的映射会覆盖更宽泛的映射user/delete覆盖user/*user/*覆盖*多个 policy 用数组。利用内置值true放行、false拒绝二者不能与其他 policy 混用生产环境建议把全局默认从*: true改为*: false。利用源码机制排障policy 名必须与api/policies/下的文件名一致引用不存在的 policy 会导致启动失败Unknown policy。配置错误E_INVALID_POLICY_CONFIG同样会在启动时被拦截而不是在请求时才暴露。相关深入阅读Policies 概念文档、Access Control and Permissions、config/policies.js 解剖、策略 hook 源码、action middleware 注册、路由绑定与匹配。赞分享后端【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址https://gitcode.com/gh_mirrors/sa/sails点击查看免费下载相关推荐Kubernetes权威指南配置Kubelet API的RBAC授权机制Kubernetes权威指南配置Kubelet API的RBAC授权机制 引言为什么需要Kubelet API的精细授权控制 在Kubernetes集群中文档/教程Apache RocketMQ 权限控制ACL实战指南从配置部署到源码级原理解析Apache RocketMQ 权限控制ACL实战指南从配置部署到源码级原理解析 导读 Apache RocketMQ 的权限控制Access Cont消息队列后端微服务流处理Sails 之 config/local.js本地开发环境配置的权威指南Sails 之 config/local.js本地开发环境配置的权威指南 导读 config/local.js 是 Sails 应用中专门用于承载 个人本地环后端上一篇Zotero Style让文献管理变得高效实用的视觉化插件指南下一篇Byzer Notebook详解数据科学家的理想工作环境创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Atlas 300V 24G部署YOLO实战:从ONNX转OM到AscendCL推理全流程

Atlas 300V 24G部署YOLO实战:从ONNX转OM到AscendCL推理全流程

1. 先搞明白Atlas 300V 24G是块什么卡1.1 它不是显卡,却总被当成显卡用很多人拿到Atlas 300V 24G的第一反应是“这玩意儿是不是类似RTX 3090的东西”,实际上这个理解从一开始就走偏了。Atlas 300V 24G是昇腾生态里一款面向AI推理场景的加速卡&#xff0c…

2026/9/20 14:10:43 阅读更多 →
stitch:在 Jupyter 中实现 Jupyter 内核与 JavaScript 双向通信的官方 Widget 实战指南

stitch:在 Jupyter 中实现 Jupyter 内核与 JavaScript 双向通信的官方 Widget 实战指南

大模型提示工程AI Agent 【免费下载链接】guidance A guidance language for controlling large language models. 项目地址: https://gitcode.com/gh_mirrors/gu/guidance 点击查看 免费下载 导读 stitch 是 guidance 项目官方仓库中附带的一个 Jupyter Widget 包…

2026/9/20 14:10:43 阅读更多 →
RVC 实战:10分钟录音训出可换声色的语音模型,4G显存就够

RVC 实战:10分钟录音训出可换声色的语音模型,4G显存就够

RVC 实战&#xff1a;10分钟录音训出可换声色的语音模型&#xff0c;4G显存就够 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrieval-based-…

2026/9/20 14:10:43 阅读更多 →

最新新闻

MicroPython pyboard 入门指南:硬件布局、供电方式与首次上电

MicroPython pyboard 入门指南:硬件布局、供电方式与首次上电

嵌入式语言运行时编程语言解释器编译器物联网系统编程 【免费下载链接】micropython MicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems 项目地址&#xff1a; https://gitcode.com/gh_mirrors/mi/micropython 点击查看…

2026/9/20 18:08:11 阅读更多 →
Flow 中利用 match 表达式一次初始化多个变量:以 applyTheme 为主题的实战指南

Flow 中利用 match 表达式一次初始化多个变量:以 applyTheme 为主题的实战指南

开发工具静态分析代码质量 【免费下载链接】flow Adds static typing to JavaScript to improve developer productivity and code quality. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/flow30/flow 点击查看 免费下载 本指南以 Flow&#xff08;项目根目录&#x…

2026/9/20 18:08:11 阅读更多 →
SpringBoot智能仓储系统实战:从毕设到工业级落地

SpringBoot智能仓储系统实战:从毕设到工业级落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 18:08:11 阅读更多 →
微信Windows旧版本回退指南:历史安装包获取、兼容性验证与数据迁移

微信Windows旧版本回退指南:历史安装包获取、兼容性验证与数据迁移

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 18:08:11 阅读更多 →
安桥TX-NR636说明书实战:接线、AccuEQ校准与常见故障排查

安桥TX-NR636说明书实战:接线、AccuEQ校准与常见故障排查

简介&#xff1a;这是一份安桥TX-NR636功放的中文高级使用说明书&#xff0c;面向拥有该型号功放、希望充分挖掘其功能的中高级用户及家庭影院爱好者。内容涵盖AM/FM自动与手动调台、RDS电台信息显示、USB存储设备音乐播放、网络收音机&#xff08;TuneIn&#xff09;与DLNA串流…

2026/9/20 18:08:11 阅读更多 →
Flow 模式匹配实战:用 Tuple Pattern 同时匹配多个参数(tooltipPosition 示例剖析)

Flow 模式匹配实战:用 Tuple Pattern 同时匹配多个参数(tooltipPosition 示例剖析)

开发工具静态分析代码质量 【免费下载链接】flow Adds static typing to JavaScript to improve developer productivity and code quality. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/flow30/flow 点击查看 免费下载 导读 本文以 Flow 官方评估套件&#xff08;…

2026/9/20 18:07:11 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事&#xff1a;用Flutter给OpenHarmony做一款游戏集合类的App&#xff0c;说白了就是把若干小游戏塞进一个壳里&#xff0c;用统一入口分发。这个方向本身不算新鲜&#xff0c;真正让我花了不少心思的&#xff0c;是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档&#xff0c;最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事&#xff1a;今天在表后面多加了两个空白行&#xff0c;明天给客户交稿前发现整个章节的编号全部错位&#xff0c;光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年&#xff0c;说实话&#xff0c;第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年&#xff0c;流量惨淡、功能臃肿、代码自己都懒得看第二遍之后&#xff0c;我才慢慢琢磨明白一个道理&#xff1a;第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事&#xff1a;用Flutter给OpenHarmony做一款游戏集合类的App&#xff0c;说白了就是把若干小游戏塞进一个壳里&#xff0c;用统一入口分发。这个方向本身不算新鲜&#xff0c;真正让我花了不少心思的&#xff0c;是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档&#xff0c;最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事&#xff1a;今天在表后面多加了两个空白行&#xff0c;明天给客户交稿前发现整个章节的编号全部错位&#xff0c;光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年&#xff0c;说实话&#xff0c;第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年&#xff0c;流量惨淡、功能臃肿、代码自己都懒得看第二遍之后&#xff0c;我才慢慢琢磨明白一个道理&#xff1a;第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践&#xff1a;原型怎样变成可用功能分类&#xff1a;[AI/大模型]细分主题&#xff1a;AI 增强型 CI/CD 流水线自动化与 GitOps 实践&#xff1a;Agent 工作流、工具调用与任务拆解&#xff1a;从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战&#xff1a;复盘记录怎样真正派上用场分类&#xff1a;[工程技术]细分主题&#xff1a;Kubernetes 生产环境运维与排障实战&#xff1a;可复制的项目复盘模板与决策记录大部分团队的事故复盘报告&#xff0c;最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理&#xff1a;核心链路应该先拆哪一步分类&#xff1a;[工程技术]细分主题&#xff1a;Docker 容器化技术与镜像安全管理&#xff1a;核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用&#xff08;包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →