访问控制和 XHR 请求

Posted

技术标签:

【中文标题】访问控制和 XHR 请求【英文标题】:Access control and XHR requests 【发布时间】:2013-05-04 00:18:27 【问题描述】:

我正在为自定义框架的访问控制实现而苦苦挣扎。

不需要 RBAC 粒度,所以我决定使用某种 ACL 来存放资源 控制器动作。

这是数据库结构:

用户:

约翰 玛丽 格雷格

用户组:

管理员 会计师 经理

users_to_user_groups:

John => 管理员 玛丽 => 会计师 格雷格 => 经理

资源(控制器操作):

用户/编辑 发票/添加 客户/删除

resources_to_user_groups:

用户/编辑 => 管理员 发票/添加 => 会计师 客户/删除 => 经理

这是[伪]代码。

$user = new User; // This will be currently logged in user ...

$acl = new Acl($user);

$dispatcher = new Dispatcher($acl);

$dispatcher->dispatch('users', 'new');

class Dispatcher

    public function dispatch($controller, $action)
    
        $permission = $controller . '/' . $action;

        if(!$this->acl->isAllowed($permission))
        
            throw new AccessDeniedException("Access denied");
        

        // User is authorized to execute this action, dispatch ...
    

我喜欢这种方法...直到我意识到还有很多 XHR 请求。

例如,发票列表使用 XHR 请求获取总金额,订单列表使用 XHR 请求 加载订单位置和其他数据等。

所以,一定有一些资源分组,比如新表resource_groups:

发票清单(invoices/list, invoices/xhr_get_total_amount) 订单列表(orders/list、orders/xhr_get_positons_for_order、orders/xhr_get_some_other_data) 添加新用户 (users/new) # 单个操作,新用户输入表单不使用 XHR 请求

...而不是将资源分配给用户组,而是将资源组分配给用户组。

感觉好复杂。这是正确的方法吗?有什么可以改进的?有什么框架可以解决这个问题吗?

【问题讨论】:

为什么 XHR 很重要?具有相同权限的同一用户以相同的方式访问相同的资源,只是打包并以不同的方式交付给客户端。如果还有更多内容,那么这里几乎没有足够的信息能够为您提供任何有意义的帮助。 Jason - 因为单个“资源”(更好的措辞是 DudeOnRock 提到的“活动”)可以包含单个静态 HTTP 请求,或者另外调用一个或多个 XHR 请求。因此,我们不能假设每个 controller::action 对都是“权限”,我们需要以某种方式对它们进行分组。 我觉得你好像在某个地方转错了方向。如果您基于“请求”创建权限(或者,尽可能直截了当,每个 URL 都有自己的特定权限),那么您将走上一条无法维护的道路。创建组以添加这些请求只是稍微抽象了维护问题。现在,您不会是第一个采用这种方法的人(我工作中使用的遗留系统,最初基于 osCommerce,控制每个文件名的访问),但是如果您的应用程序现在或希望变得更简单大小,你会头疼的。 是的,你对我的初始计划是正确的,每个控制器::动作对(或 URL)都需要单独的权限。但正如我在帖子末尾提到的,我认为需要将一些操作组合在一起以形成一个单一的权限/活动。你建议的方法是什么?我给你一个例子 - 活动“查看日志”,它由 2 个控制器操作组成 - 一个静态 HTTP 请求,它绘制一些表格 html 标记,在这个页面中也是一个 XHR 请求,它获取最新的日志记录,比如说,每 30 秒。你如何在单一权限下合并它们? @LaurisB:你的 XHR 请求可以调用几个不同的函数来代表接收脚本中的一个活动。如果其中一些功能不需要权限,那很好,如果所有功能都需要,则单独验证每个活动。 【参考方案1】:

在过去的一年里我一直在处理同样的问题,这就是我解决它的方法。

首先,我使用Zend Framework's ACL 库作为基础引擎来告诉我某个用户是否可以访问某些资源。由于 ZF 已经支持用户分组和角色(包括角色层次结构),您无需再担心。

抛开用户分组和角色,接下来是 ZF 内部不支持的资源分组(遗憾的是)。我相信这是您的问题所关注的部分。然而,您可以使用 ZF 并对其进行扩展以满足您的需求。您需要做的就是为资源(平面或分层)提出一个分组机制。然后您可以按照 ZF 手册中的说明使用 ZF。

下面是一个例子:

    构造ACL引擎和其他基本对象:

    $acl = new Zend_Acl();
    
    $acl->addRole(new Zend_Acl_Role('guest'))
        ->addRole(new Zend_Acl_Role('member'))
        ->addRole(new Zend_Acl_Role('admin'));
    
    $parents = array('guest', 'member', 'admin');
    $acl->addRole(new Zend_Acl_Role('someUser'), $parents);
    

    定义您的资源分组:

    $resources = array(
        'group 1' => array(
            'resource 1'
            , 'resource 2'
            , 'resource 3'
        )
        , 'group 2' => array(
            'resource 1'
            , 'resource 4'
            , 'resource 5'
        )
    );
    

    将您的资源引入 ACL 的引擎:

    function addResource(Zend_Acl $acl, $resources, $groupName)
    
        foreach ($resources[$groupName] as $resource) 
            $acl->add(new Zend_Acl_Resource($resource));
        
    
    
    addResource($acl, $resources, 'group 2');
    

    使用ACL的引擎查询权限:

    echo $acl->isAllowed('someUser', 'resource 1') ? 'allowed' : 'denied';
    

如你所见,我在这里没有做任何特别的事情。这里引入的唯一新概念是添加到 ACL 引擎的资源与用于查询它的资源不同。但是在我们实际调用 ZF 的库之前已经完成了一层,因此查询 ZF 的 ACL 的调用仍然有效。

我希望我能清楚地写下我的观点。别忘了我只是想给你一个概念,你需要自己想出实际的实现。

【讨论】:

是的,这与我对资源分组的想法相似。我将使用自定义实现,尽管资源组将是“权限”。 我个人认为资源层次结构更有用。在这种情况下,您需要记住的是使用路径而不是组名。就像/branch 1/branch 1.1 一样,这意味着您在php 中有一个类似$resources['tree']['branch 1']['tree']['branch 1.1'] 的数组。并且您需要将数组$resources['resources']$resources['tree']['branch 1']['resources']$resources['tree']['branch 1']['tree']['branch 1.1']['resources'] 组合在一起,以制作它们附带的资源列表。我相信您可以自己弄清楚使用treeresources 条目的必要性。【参考方案2】:

为什么不直接向resources 表添加新列,称为descendants,它将存储一个依赖xhr 资源的数组?所以它可能看起来像:

resource: "invoices/list"
descendants: ["invoices/xhr_get_total_amount"]

resource: "orders/list"
descendants: ["orders/xhr_get_positons_for_order","orders/xhr_get_some_other_data"]

resource: "users/new"
descendants: []

此外,如果尚未实现:您可以在请求开始时获取并处理允许资源及其后代的整个列表到单个数组,并将它们存储到响应。因此,您不会在每次需要检查访问权限时都询问数据库。

【讨论】:

【参考方案3】:

我知道我不会直接回答您的问题,但也许我可以让您选择一条稍微不同的路径,一旦您的网站规模和复杂性增加,这条路径会变得更加灵活。在阅读this 帖子后,我已将身份验证系统切换为基于活动(允许活动“doSomething”),而不是基于角色(“管理员”可以执行以下操作......)。

只是一个想法。

【讨论】:

当某些“活动”使用 XHR 时,您如何处理权限管理?例如,除了 HTML 表单渲染 (users/new) 之外,“添加新用户”活动使用 XHR 获取一些数据(例如,更改 HTML 表单的国家/地区组合,获取该国家/地区的城市,例如 users/xhr_get_cities_by_country_id)。跨度> 活动“addNewUser”不应对任何输出本身负责。它应该只是向数据库添加一个新用户。 我的错,“添加新用户”我想到了新用户表单。就像在 REST 命名中一样:users/new (form) => users/create (add to DB) and users/edit/5 (form) => users/update/5 (update to DB)。【参考方案4】:

最好不要使用 * 而是使用请求文件的域。 * 在某些版本的 IE 中会失败(不记得了)。

if ($ref_url)
  header("Access-Control-Allow-Origin:"+$ref_url);
else
  header("Access-Control-Allow-Origin:*");

【讨论】:

以上是关于访问控制和 XHR 请求的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 存储和访问控制允许来源

XMLHttpRequest 成功,而 fetch 被阻止

SOAP 请求的问题。 '访问控制允许来源'

nginx 请求限制和访问控制

预检请求未通过访问控制检查:

Sphere Engine 问题 API - 错误:对预检请求的响应未通过访问控制检查:不存在访问控制允许来源