为啥 auth 通常在 MVC 的 Controller 中?

Posted

技术标签:

【中文标题】为啥 auth 通常在 MVC 的 Controller 中?【英文标题】:Why is auth typically in the Controller in MVC?为什么 auth 通常在 MVC 的 Controller 中? 【发布时间】:2013-10-27 17:59:00 【问题描述】:

我已经为不同的 MVC 框架做了大量的教程,而且在控制器中进行授权似乎很典型。为什么?

我的想法是控制器应该只用于编排模型操作、处理重定向和处理错误事件。这些是依赖于特定请求的东西。将授权放在控制器中似乎每当您在不同的控制器操作或不同的控制器中使用相同的模型操作时,您都必须复制授权。如果 Auth 在模型中,则您对数据执行操作或状态更改具有一致的要求。

我一直在谷歌搜索并查看其他问题,例如 Should authorization be part of the model or controller?,但我真的不明白为什么这是公认的约定。

将授权放在模型上的控制器中是否有我遗漏的特定原因?

在cmets中总结点:

控制器负责改变模型层和当前视图的状态。没有别的了。 授权属于执行操作的地方,如果您遵循严格的 MVC 模式,这很可能是模型,并且控制器当然不负责授权使用模型操作。 应该像对待任何其他数据存储一样对待 Cookie:在模型中抽象和使用,而不是直接由控制器使用。 身份验证和授权是单独的问题,尽管它们通常都进入模型层,因为它们通常涉及对数据存储区中的值(例如 cookie)的检查。

【问题讨论】:

我想说,neither 控制器和模型层都不应该与授权检查有关。人们大多将其转储到“控制器”中,因为在使用类似 Rails 的架构时,他们会在那里转储其他所有内容。 我正在使用一个有授权规则的类,但你实际上在哪里执行?如果不在控制器或模型中,你在哪里说“要这样做,你必须通过这些规则”? 嗯。我在您的评论中读到有规则。我想说的是,这些规则应该已经为您提供了一些与它们相关的指针。 @Charles ,我实际上认为授权(就像记录用户操作一样)应该是“高于”实例,它会影响。假设您要验证用户是否可以访问AdminPanel::deleteEverything()。应用程序初始化AdminPanel 实例以检查用户是否有权访问该类中的方法是否有意义?另一方面:如果您更改某些身份验证过程,您是否必须手动检查所有类,看看是否不会有任何问题?在我看来,这种方法会违反 SRP 和 OCP。 如果不在模型或控制器中,它会去哪里?对我来说,在他们自己的类中有授权规则,并有一个模型操作调用,比如 $this->auth->requireAdmin(..); 是有意义的。如果您想更改需要管理员的身份验证规则,它在一个地方。 【参考方案1】:

将授权放在模型上的控制器中是否有我遗漏的特定原因?

嗯,我能想到的最常见的原因是 懒惰。我并不是说在道德上,将一些授权概念放在更接近具体请求的层中,然后在模型层上进行差异化访问要容易得多。获得模型授权是一个更高的设计。

为了在答案中添加一些更实用的建议,我认为您应该针对每个程序分析您希望在何处以及要引入授权的内容。对此的需求可能(非常)不同。

那么只有在下一步中,您才应该考虑哪种设计最有利于引入授权和身份验证来满足这些需求。

【讨论】:

只是稍微扩展一下这个答案,想象一下如果您通过多个控制器发送请求,其中也包括 APIControllers,那么您将必须在两个控制器中执行此操作(并为两组函数设置属性)。如果您的业务逻辑向控制器抛出一些错误,导致它们执行一些默认错误操作并将错误详细信息写入 Viewbag,则这一切都可以忽略。【参考方案2】:

在 MVC 方法中,您需要将安全性置于以下位置:

    无法绕过 可以轻松配置、管理和更新

事实上,这适用于任何架构/类型的应用程序。

具体来说,在 MVC 中,假设您将授权放在视图中。例如,您决定通过启用/禁用按钮来控制谁可以批准交易。如果不允许,您视图的用户将无法批准交易。现在想象一下,您通过 API 而不是视图公开您的控制器。现在需要在 API 层重新实现批准授权检查。

这个例子表明你最好将授权从视图/不同的端点转移到一个公共的中心点——你的控制器。

同样,如果您想控制对大量数据(例如医疗记录)的访问,您最好将授权放在模型中。这既是出于性能原因,也是出于安全原因:您宁愿让控制器处理较少的数据,并且应始终努力保护尽可能接近敏感数据的来源。

请注意,同时在视图、控制器和模型中使用授权钩子/检查可能会带来完全增强的体验。将视图中的授权视为“安全/可用性”机制,用户仅根据其权限在屏幕上显示那些相关的菜单和小部件。如果他们是恶意的并且知道如何绕过 UI 到达控制器,授权仍然会启动。

最后,一般来说,您希望将非功能性需求/逻辑与功能性需求/逻辑分离。就像您没有在代码中实现日志记录而是使用可配置框架(例如 Log4J)或者您依赖容器进行身份验证(例如 Apache Tomcat 中的 HTTP BASIC),您希望使用外部化授权框架,例如基于声明的授权Microsofct MVC4 世界、Java 中的 Spring Security、Ruby 中的 CanCan 或 XACML,它是与 SAML (OASIS) 相同主体的标准部分,可让您将授权应用于任何类型的应用程序和任何层。

【讨论】:

【参考方案3】:

授权作为一个完整的过程应该涉及到:控制器层和模型层。

但是,所有的 logc(SQL 查询等)都应该发生在模型中。 控制器是视图(表示)和模型之间的一种中间层。 但是,您根本无法从该方案中丢弃 Controller,因为 Controller 负责处理 Sessions 和 Cookies。没有这两件事,您的所有身份验证/授权逻辑都是无用的,因为它本质上是无状态的。会话和 Cookie 为其带来状态。 此外,正如您正确提到的,Controller 负责重定向。

【讨论】:

-1:控制器不在模型和视图之间进行中间处理,控制器与会话和 cookie 无关(这些是存储形式)。身份验证和授权也是独立的职责。重定向是一种响应形式。它是一个“输出”,仅包含一个 HTTP Location 标头。 控制器是您的 HTTP 请求的端点。所以它还负责渲染布局/模板。 要点是控制器负责改变模型层和当前视图的状态。没有其他的。在 MVC 模式中,视图负责渲染和管理模板,因为视图的职责是处理 UI 逻辑。 理想情况下,无论是 HTTP 还是其他类型的请求都无关紧要。控制器不应特定于请求类型。 MVC 与底层服务器/平台行为无关。这是关于分离责任。当您调用 setcookie() 时,它不会对标题做任何事情。没有。它只告诉 php:“当你要发送标头时使用这个参数”。这里的重点是 cookie 存储信息。映射到模型层内的各种域对象的信息。模型绝不会与 HTTP 响应交互。

以上是关于为啥 auth 通常在 MVC 的 Controller 中?的主要内容,如果未能解决你的问题,请参考以下文章

在 MVC 中使用 Access-Control-Allow-Origin

来自 CORS 预检通道的 CORS 标头“Access-Control-Allow-Headers”中缺少令牌“x-auth”[重复]

MVC .Net 项目在真实服务器上部署时没有“Access-Control-Allow-Origin”标头

为啥我的 ASP.NET MVC 站点中的此 AJAX 请求会触发预检检查?

角度 2 与 auth0 读取所有用户错误 对预检请求的响应未通过访问控制检查:没有“Access-Control-Allow-Origin”

MVC与WEB应用