用户权限检查应该在哪里进行,并且MVC由谁进行?


26

用户权限检查应该在模型或控制器中进行吗?谁应该处理权限检查,User对象或某些UserManagement助手?

应该在哪里发生?

检入控制器:

class MyController {
  void performSomeAction() {
    if (user.hasRightPermissions()) {
      model.someAction();
    }
  }
  ...

在Controller中进行检查有助于简化模型的操作,因此我们可以将所有逻辑保留在Controller中。

签入模型:

class MyModel {
  void someAction() {
    if (user.hasRightPermissions()) {
      ...
    }
  }
  ...

通过将检查放到模型中,我们使模型复杂化,而且还确保我们不会意外允许用户执行他们不应该在Controller中进行的操作。

又是谁?

一旦我们确定了该地点,谁应该进行检查?用户?

Class User {
  bool hasPermissions(int permissionMask) {
    ...
  }
  ...

但是,了解自己的能力并不是用户的真正责任,所以也许是一些帮助者?

Class UserManagement {
  bool hasPermissions(User user, int permissionMask) {
    ...
  }
  ...

我知道在一个问题中只问一个问题是很常见的,但是我认为可以很好地回答这些问题。

Answers:


20

像往常一样,“取决于”

  • 权限检查可以 在任何方便的地方起作用
  • 但是,如果您要提出技术问题,那么答案可能是“将检查放入拥有执行检查所需数据的对象中”(可能是控制器)。
  • 但是,如果您要提出一个哲学问题,我建议您提供一个替代答案:不要向用户显示不允许其执行的操作

因此,在后一种情况下,您可能在控制器中将权限检查实现为布尔属性,并将该属性绑定到控制操作的用户界面中的按钮或面板的Visible属性

作为用户,看到无法执行的操作的按钮令人沮丧;感觉就像我被淘汰了;)


我们的应用程序实现了第三种情况,除了我们不隐藏控件,我们禁用了它们。不幸的是,这些都是在Winforms后台代码中完成的,因此它与OP问题并没有真正的关系。
戴夫·奈

11
“看到我无法执行的操作的按钮很令人沮丧” ->尝试对自己的帖子进行投票:)
Rowan Freeman

5
仅隐藏用户无法执行的操作的按钮是不够的,服务器必须检查每个请求的权限。第三个项目符号不是“替代答案”,除了检查服务器端权限之外,还需要执行其他操作。
Flimm

@Flimm同意,如果请求是由服务器处理的;具体的问题是关于控制器类的
Steven A. Lowe

7

安全是一个贯穿各领域的问题,因此需要在多层中实现。以下是MVC的示例,但是该概念适用于其他体系结构和/或模式,您只需确定执行要点即可。

应该在哪里发生?

视图可能包含基于某些用户的权限需要显示或不显示给某些用户的UI元素(小部件,按钮,菜单等)。这可能是视图引擎的责任,因为您不希望每个视图都自行处理。根据您要对您进行授权的元素类型,将此责任移到另一个地方。例如,考虑一个菜单,其中某些项目必须显示而有些则不需要。这些项目可以实现为某处的列表,并根据权限过滤该列表,然后将其转发到视图。

控制器响应请求,因此,如果用户没有执行操作的权限,则应在调用操作之前对其进行检查,将责任移交操作调用者,而不是将其保留在控制器中。这具有保持控制器整洁的优势,并且如果权限中发生某些更改,则无需筛选控制器即可应用这些更改。

资源根据权限显示。这通常是在数据库级别完成的,因为您不想从数据库中提取所有内容,然后再应用权限。

如您所见,根据您要授权的内容,应该在不同的位置进行此操作。我们的目标是尽可能不打扰,以便在更改安全策略时可以轻松地应用它,最好不要更改应用程序的代码。这对于小型应用程序可能无效,在小型应用程序中,权限集很小,并且不会经常更改。但是在企业应用程序中,情况却大不相同。

谁应该做?

显然不是模型。每个层都应具有一个处理授权的执行点。上面的斜体文本突出显示了每个级别的可能执行点。

看看XACML。您不必按原样实施它,但是它将为您提供一些可以遵循的指导。


这是最好的答案。由于某种原因,高层管理人员和其他人处理控制器与视图或视图与模型之间的差异,而这并不是OP所要求的。谢谢!
redFur

1

我使用以下方案。值得一提的是,大多数用户权限检查可以分为两种一般情况:

  • 用户根据用户角色访问控制器操作,而无需检查参数操作,
  • 用户根据特定用户与特定模型之间的任何逻辑或关系访问模型。

通常在MVC框架中实现对控制器动作的访问而不检查属性。这一点很简单:您定义规则,您的用户就可以扮演角色。您只需检查用户是否有权进行操作来查找其在规则中的角色。

用户对特定模型的访问应在模型中定义。(Actor是基本用户类。假设它可以是客户,卖方或访客。)

interface ICheckAccess
{
    public function checkAccess(Actor $actor, $role);
}

class SomeModel implements ICheckAccess
{
    public function checkAccess(Actor $actor, $role)
    {
        // Your permissions logic can be as sophisticated as you want.
    }
}

将逻辑放在模型中会带来一些好处。访问检查方法可以被继承,您不需要创建任何额外的类,就可以使用常规的OOP优势。

接下来,为了简化访问检查,为了简单起见,我们采取一些假设,这些假设几乎总是已经实现:

  • 通常,控制器与某些模型类相关;
  • 检查访问的操作以单个模型ID作为参数;
  • 始终可以从基本控制器类的方法统一访问此参数;
  • 动作放置在与ID动作所采用的模型相对应的控制器中。

通过这些假设,可以将使用模型ID的动作与特定的模型实例相关联。实际上,大多数动作都可以轻松地转换和移动以适应上述假设。

然后,应该定义和继承一些基本的抽象控制器类。

abstract class ModelController
{
    // Retrieve model from database using id from action parameter.
    public abstract function loadModel($id);

    // Returns rules for user role to pass to SomeModel::checkAccess()
    // Something like array('view' => 'viewer', 'delete' => 'owner', 'update' => 'owner')
    public abstract function modelRules();

    public abstract fucntion getIdParameter();

    public function filterModelAccess()
    {
        $id = $this->getIdParameter();
        if(!$this->checkModelAccess($id))
            throw new HttpException(403);
    }

    public function checkModelAccess($id)
    {
        $model = $this->loadModel($id);
        $actor = My::app()->getActor();
        $rules = $this->modelRules();
        $role = $rules[My::app()->getActionName()];
        return $model->chechAccess($actor, $role);
    }
}

您可以在构造菜单并决定是否显示某些链接时调用SomeController :: checkModelAccess($ id)方法。


对不起,PHP。
乔治·索维托夫

1

在模型视图中

在视图中 -因为用户界面不应显示仅限于当前用户的用户界面元素

(例如,应该向具有适当权限的人显示“删除”按钮)

在模型中 -因为您的应用程序可能具有某种API,对吗?该API还必须检查权限,并且可能会重用该模型。

(例如,您同时在用户界面中具有“删除”按钮 “ http:/ server / API / DeleteEntry / 123” API方法


为什么选择模型而不是控制器?
Flimm

不知道为什么要在大多数时间完成视图,建模而不是在控制器中进行建模。
副总裁

@VP控制器无权显示/隐藏UI元素(除了将bool-var传递给视图外)
jitbit,

我不知道,通常在控制器层中都可以完成所有工作,这就是为什么我很好奇的原因。
副总裁

0

MVC是一种表示模式。因此,视图和控制者仅应负责表示。某些权限适用于演示文稿,例如专家模式,实验性UI功能或不同的设计。这些可以由MVC控制器处理。

许多其他类型的权限与应用程序的多个层有关。例如,如果您希望拥有只能查看数据而不能更改内容的用户:

  • 表示层必须隐藏编辑功能
  • 如果仍然调用了编辑功能,则可能(应该)检测到此功能(业务层的应用程序特定部分,而不是域的特定部分-TrainEditor,而不是Train),并且可能导致异常
  • 数据访问层也可以检查写操作,但是对于较复杂的权限,这种权限很快需要对业务层有太多了解才是一个好主意。

这种方法有些重复。但是由于表示通常是易变的,因此可以为检查通常在应用程序更稳定部分中的许可提供一个很好的理由,即使这意味着在表示层按预期工作的情况下进行一些多余的检查。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.