在 MVC Web 应用程序中的何处放置授权?

Where to place authorisation within an MVC web application?

场景

您正在为营销部门构建 Web 应用程序。市场营销 部门已经申请了一个博客平台,以便他们可以留住您的客户 了解最新的公司新闻。您决定构建一个传统的 MVC 具有关系数据库的 Web 应用程序。您选择 LAMP (Linux Apache PHP MySQL) 堆栈或类似的。

营销团队的要求很简单。他们想要:

根据这些要求,您决定实施 RBAC(基于角色的访问 控制)。您将角色定义为:

您还创建了一个 "Posts Controller",其中有五个操作:

最后你设置了一个身份验证系统。认证系统将 始终 return 您实施的角色之一。如果用户已登录,则 你会得到 "Manager" 或 "Marketer"(取决于他们的工作 在您的公司内)。如果用户没有登录,你会得到一个 "Guest".

在您开始为 "Update" 行动。

问题

该网站中的 "Update" 操作应该在哪里进行授权 申请?

请确保不要混淆身份验证(检查用户是否已登录和 确定他们是谁)授权(检查用户是否允许 做他们想做的事)。

可能的答案

  1. 模型。

    • 因为查不到原作者所以无法进入ac​​tion 博客 post,直到您调用模型。
    • 你的业务逻辑应该进入模型以坚持“瘦 控制器,胖模型”经验法则。
    • 授权不应该发生在行动中,因为我们最终会 在调用相同模型的多个操作中复制我们的逻辑 方法。
  2. 动作。

    • 授权不能在模型中发生,因为并非所有操作都使用 模型。 "Create" 动作怎么样?它可能只呈现 HTML 表单。 这意味着授权被跳过。
    • 将用户对象传递给每个模型方法似乎很麻烦。不是这样的 该操作是因为它知道用户所在的 HTTP 会话 已存储。
    • 计划任务或 cronjob 运行时会发生什么? RBAC 在这里没有立足之地 除非我们实施 "System" 角色。如果授权发生在 action 计划任务不需要知道角色。
    • 如果到模型时检查授权已经来不及怎么办 方法被调用?被调用的方法可能是调用的三个方法之一 一种行为。如果前两次通过而第三次失败,你可以介绍 错误到您的网络应用程序。预先进行所有授权检查 避免了这个。
  3. 动作和模型。

    • 将授权拆分为 "Role based authentication" 和 "Business logic authentication" 可能有效。但是你会在哪里画 线?
  4. 其他地方。

    • 我的 MVC Web 应用程序中是否缺少一个层?服务层或 中间件有帮助吗?

根据我的经验,当你想到授权管理时,你必须经常想到一个 Aspect 理解为 AOP 的一个元素范式(或类似的东西)。所以它是位于所有应用程序层之上但采用非侵入式方法的东西。

谈到授权管理应该放在什么地方,我认为它必须放在一个共同的地方,您的"action endpoints" 将被调用和管理。例如,如果你有休息或肥皂 api,所有授权管理都可以放在第一个请求处理程序中,以便在调用每个控制器操作之前拒绝未经授权的用户。

回到具有 3 层的简单 MVC 应用程序,您可以定义在每个控制器操作之前调用的 AuthManager。 如果您的授权管理还涉及一些视图动态修改,则应使用不同且更具侵入性的方法,但这是一个不同的问题。

经过大量搜索和研究,我想我找到了自己问题的答案。

为了满足我需要实现的要求"Attribute Based Access Control"(缩写为ABAC)。它也被称为 "Claims Based Access Control" 和 "Policy Based Access Control"。

ABAC 使用四个输入来决定是否允许当前用户执行他们正在尝试执行的操作。四个输入是:

  • 主题 - 这是用户对象。它将包含用户 ID 及其角色。
  • 动作 - 这将是控制器名称和动作名称。
  • 资源 - 这将是我给出的示例中的博客 post 对象。
  • 环境 - 这可能会在我给出的示例中得到解决。但是您可以使用它来区分 Web 应用程序、API 或计划任务。

检查应该发生在动作中。这是一个快速的 PHP 伪代码示例:

class PostsController {
    public function updateAction() {
        // get the post
        $model = new PostsModel();
        $post = $model->findById($_GET['id']);
        // check authorisation
        $authorised = $this->authorisor->check(
            $this->user,
            "PostsController::updateAction",
            $post,
            APP
        );
        // build response etc.
    }
}

为什么它需要在动作中发生应该是显而易见的;没有资源(上例中的 post)来检查其他情况!