如何在我的 Web MVC 应用程序中实现访问控制列表? [英] How can I implement an Access Control List in my Web MVC application?

查看:21
本文介绍了如何在我的 Web MVC 应用程序中实现访问控制列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第一个问题

请您解释一下如何在 MVC 中实现最简单的 ACL.

Please, could you explain me how simpliest ACL could be implemented in MVC.

这是在控制器中使用 Acl 的第一种方法...

Here is the first approach of using Acl in Controller...

<?php
class MyController extends Controller {

  public function myMethod() {        
    //It is just abstract code
    $acl = new Acl();
    $acl->setController('MyController');
    $acl->setMethod('myMethod');
    $acl->getRole();
    if (!$acl->allowed()) die("You're not allowed to do it!");
    ...    
  }

}
?>

这是非常糟糕的方法,缺点是我们必须在每个控制器的方法中添加一段 Acl 代码,但我们不需要任何额外的依赖项!

It is very bad approach, and it's minus is that we have to add Acl piece of code into each controller's method, but we don't need any additional dependencies!

下一步是将控制器的所有方法设为private,并将ACL代码添加到控制器的__call方法中.

Next approach is to make all controller's methods private and add ACL code into controller's __call method.

<?php
class MyController extends Controller {

  private function myMethod() {
    ...
  }

  public function __call($name, $params) {
    //It is just abstract code
    $acl = new Acl();
    $acl->setController(__CLASS__);
    $acl->setMethod($name);
    $acl->getRole();
    if (!$acl->allowed()) die("You're not allowed to do it!");
    ...   
  }

}
?>

它比以前的代码好,但主要缺点是...

It is better than previous code, but main minuses are...

  • 所有控制器的方法都应该是私有的
  • 我们必须将 ACL 代码添加到每个控制器的 __call 方法中.

接下来的做法是将 Acl 代码放入父控制器中,但我们仍然需要将所有子控制器的方法保持私有.

The next approach is to put Acl code into parent Controller, but we still need to keep all child controller's methods private.

解决办法是什么?最佳实践是什么?我应该在哪里调用 Acl 函数来决定允许或禁止执行方法.

What is the solution? And what is the best practice? Where should I call Acl functions to decide allow or disallow method to be executed.

第二个问题

第二个问题是关于使用 Acl 获取角色.假设我们有客人、用户和用户的朋友.用户对查看他的个人资料的访问权限受到限制,只有朋友才能查看.所有来宾都无法查看此用户的个人资料.所以,这是逻辑..

Second question is about getting role using Acl. Let's imagine that we have guests, users and user's friends. User have restricted access to viewing his profile that only friends can view it. All guests can't view this user's profile. So, here is the logic..

  • 我们必须确保被调用的方法是配置文件
  • 我们必须检测此个人资料的所有者
  • 我们必须检测查看者是否是此个人资料的所有者
  • 我们必须阅读有关此个人资料的限制规则
  • 我们必须决定执行或不执行配置文件方法

主要问题是关于检测个人资料的所有者.我们只能通过执行模型的方法 $model->getOwner() 来检测谁是配置文件的所有者,但 Acl 无权访问模型.我们如何实现这一点?

The main question is about detecting owner of profile. We can detect who is owner of profile only executing model's method $model->getOwner(), but Acl do not have access to model. How can we implement this?

推荐答案

第一部分/答案(ACL实现)

在我看来,解决这个问题的最好方法是使用 装饰模式,基本上,这意味着你拿走你的对象,然后把它放在里面另一个对象,它就像一个保护壳.这不需要您扩展原始类.下面是一个例子:

First part/answer (ACL implementation)

In my humble opinion, the best way to approach this would be to use decorator pattern, Basically, this means that you take your object, and place it inside another object, which will act like a protective shell. This would NOT require you to extend the original class. Here is an example:

class SecureContainer
{

    protected $target = null;
    protected $acl = null;

    public function __construct( $target, $acl )
    {
        $this->target = $target;
        $this->acl = $acl;
    }

    public function __call( $method, $arguments )
    {
        if ( 
             method_exists( $this->target, $method )
          && $this->acl->isAllowed( get_class($this->target), $method )
        ){
            return call_user_func_array( 
                array( $this->target, $method ),
                $arguments
            );
        }
    }

}

这就是你使用这种结构的方式:

And this would be how you use this sort of structure:

// assuming that you have two objects already: $currentUser and $controller
$acl = new AccessControlList( $currentUser );

$controller = new SecureContainer( $controller, $acl );
// you can execute all the methods you had in previous controller 
// only now they will be checked against ACL
$controller->actionIndex();

您可能会注意到,此解决方案有几个优点:

As you might notice, this solution has several advantages:

  1. containment 可用于任何对象,而不仅仅是 Controller
  2. 的实例
  3. 检查授权发生在目标对象之外,这意味着:
    • 原始对象不负责访问控制,遵守SRP
    • 当您收到权限被拒绝"时,您并没有被锁定在控制器内,还有更多选择

但是,此方法也存在一个主要问题 - 您无法本机检查受保护的对象是否实现和接口(这也适用于查找现有方法)或是否是某些继承链的一部分.

But, there are one major issue with this method too - you cannot natively check if secured object implements and interface ( which also applies for looking up existing methods ) or is part of some inheritance chain.

在这种情况下,您应该认识到的主要区别在于您的域对象(例如:Profile)本身包含有关所有者的详细信息.这意味着,为了让您检查用户是否(以及在哪个级别)可以访问它,它将要求您更改此行:

In this case the main difference you should recognize is that you Domain Objects (in example: Profile) itself contains details about owner. This means, that for you to check, if (and at which level) user has access to it, it will require you to change this line:

$this->acl->isAllowed( get_class($this->target), $method )

基本上你有两个选择:

  • 向 ACL 提供相关对象.但是你必须小心不要违反德米特法则:

$this->acl->isAllowed( get_class($this->target), $method )

  • 请求所有相关详细信息并仅向 ACL 提供其需要的内容,这也将使其对单元测试更加友好:

  • Request all the relevant details and provide the ACL only with what it needs, which will also make it a bit more unit-testing friendly:

    $command = array( get_class($this->target), $method );
    /* -- snip -- */
    $this->acl->isAllowed( $this->target->getPermissions(), $command )
    

  • 可以帮助您提出自己的实施方案的几个视频:

    Couple videos that might help you to come up with your own implementation:

    您似乎对 MVC 中的模型有很普遍的(而且完全错误的)理解.模型不是类.如果您有名为 FooBarModel 的类或继承了 AbstractModel 的类,那么您做错了.

    You seem to have the quite common ( and completely wrong ) understanding of what Model in MVC is. Model is not a class. If you have class named FooBarModel or something that inherits AbstractModel then you are doing it wrong.

    在适当的 MVC 中,模型是一个层,其中包含许多类.大部分类可以根据职责分为两组:

    In proper MVC the Model is a layer, which contains a lot of classes. Large part of the classes can be separated in two groups , based on the responsibility:

    (阅读更多:此处这里):

    来自这组类的实例处理值的计算、检查不同的条件、实施销售规则并完成您称之为业务逻辑"的所有其余工作.他们不知道数据是如何存储的,存储在哪里,甚至根本不知道存储是否存在.

    Instances from this group of classes deal with computation of values, check for different conditions, implement sales rules and do all the rest what you would call "business logic". They have no clue how data is stored, where it is stored or even if storage exists in first place.

    域业务对象不依赖于数据库.创建发票时,数据来自何处并不重要.它可以来自 SQL 或来自远程 REST API,甚至可以来自 MSWord 文档的屏幕截图.业务逻辑没有变化.

    Domain Business object do not depend on database. When you are creating an invoice, it does not matter where data comes from. It can be either from SQL or from a remote REST API, or even screenshot of a MSWord document. The business logic does no change.

    由这组类创建的实例有时称为数据访问对象.通常实现 Data Mapper 模式的结构(不要与同名的 ORM 混淆......没有关系).这是您的 SQL 语句所在的位置(或者可能是您的 DomDocument,因为您将它存储在 XML 中).

    Instances made from this group of classes are sometimes called Data Access Objects. Usually structures that implement Data Mapper pattern ( do not confuse with ORMs of same name .. no relation ). This is where your SQL statements would be (or maybe your DomDocument, because you store it in XML).

    除了这两个主要部分,还有一组实例/类,值得一提:

    Beside the two major parts, there is one more group of instances/classes, that should be mentioned:

    这是您和第 3 方组件发挥作用的地方.例如,您可以将身份验证"视为服务,它可以由您自己提供,也可以是一些外部代码.邮件发件人"也将是一项服务,它可能将某个域对象与 PHPMailer 或 SwiftMailer 或您自己的邮件发件人组件结合在一起.

    This is where your and 3rd party components come in play. For example, you can think of "authentication" as service, which can be provided by your own, or some external code. Also "mail sender" would be a service, which might knit together some domain object with a PHPMailer or SwiftMailer, or your own mail-sender component.

    服务的另一个来源是对域和数据访问层的抽象.创建它们是为了简化控制器使用的代码.例如:创建新用户帐户可能需要使用多个域对象映射器.但是,通过使用服务,它只需要控制器中的一两行.

    Another source of services are abstraction on to on domain and data access layers. They are created to simplify the code used by controllers. For example: creating new user account might require to work with several domain objects and mappers. But, by using a service, it will need only one or two lines in the controller.

    在制作服务时您必须记住的是,整个层应该.服务中没有业务逻辑.他们只是在处理领域对象、组件和映射器.

    What you have to remember when making services, is that the whole layer is supposed to be thin. There is no business logic in services. They are only there to juggle domain object, components and mappers.

    它们的一个共同点是服务不会以任何直接方式影响视图层,并且在一定程度上是自治的,它们可以(并且经常退出)在 MVC 结构之外使用本身.此外,由于服务与应用程序的其余部分之间的耦合极低,因此这种自我维持的结构使迁移到不同的框架/架构变得更加容易.

    One of things they all have in common would be that services do not affect the View layer in any direct way, and are autonomous to such an extent, that they can be ( and quit often - are ) used outside the MVC structure itself. Also such self-sustained structures make the migration to a different framework/architecture much easier, because of extremely low coupling between service and the rest of application.

    这篇关于如何在我的 Web MVC 应用程序中实现访问控制列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆