通过覆盖添加访问控制
如果想要使用的组件没有实现您需要的 ACL 特性怎么办?一种方法是通过 Joomla 的输出覆盖添加访问控制。
我最近完成了一个为一家网络公司做的项目,他们的客户需要为他们使用的组件添加一些访问控制。这个组件提供了一些难以找到的特性,但缺乏对 Joomla 的 ACL 支持。客户首先联系了组件的开发者,提出为他们添加 ACL 支持的工作付费。开发者拒绝了,所以他们转向第三方开发。
当有人要求我为扩展添加访问控制时,我会考虑几种选择:我的首选是找到一个提供所需功能以及现代 ACL 的不同扩展。如果我们找不到合适的替代方案,那么我会考虑通过覆盖视图添加所需的访问控制。另一种选择是通过面向对象的代码扩展组件,并将所需功能添加到新的子类中。有时客户可以接受该组件有限的访问控制。只有作为最后的手段,我才会考虑修改组件或对其进行分支。
客户承诺使用一个特定的基于注册的组件,所以我们不能寻找替代方案。客户可以在后端没有特殊权限的情况下生存,这使得项目更简单。我会利用 Joomla 的 ACL 限制谁能访问这个组件,但任何有权访问的人都可以在这个组件中创建、编辑或删除任何内容。
所需的是限制用户可以根据其注册类型查看哪些类和类别的类。因此,实际工作需要我为每个对象及其类别添加访问级别,并且内容的显示应遵循 ACL 规则。当然,解决方案必须允许客户升级组件。
理想的位置
如果我是这个扩展的开发者,我会效仿Joomla核心的例子,在组件的核心添加访问控制。考察一个核心组件如com_contact,你会发现访问控制主要在模型类中实施。这是理想的,因为模型负责从数据库检索所有数据,这使得这是保护应该提供给用户哪些数据的最佳安全方法,同时也允许或拒绝对数据进行某些操作。
模型是访问控制应该去的地方。但对于需要添加功能的第三方开发者来说,这并不简单。任何对模型的第三方代码更改都容易在未来升级中被覆盖。只有开发者可以安全地更改这里的代码。如果我们需要添加功能,要么我们分叉代码并放弃简单的升级,要么我们在不受升级影响的文件中进行更改。我更喜欢后者。
利用覆盖
Joomla允许我们通过覆盖“视图”文件安全地更改内容的显示。这些覆盖文件不能被扩展升级覆盖。虽然意图是允许有各种不同的数据布局,视图文件也可以包含执行额外处理的代码。正如我们可以控制内容的展示方式一样,可以说我们也可以控制展示的内容。我发现,可以通过覆盖视图文件注入大量的功能。
这里不解释Joomla中的输出覆盖,但以下是一个起点:http://docs.joomla.org/How_to_override_the_output_from_the_Joomla!_core
在处理访问控制时,这种方法有局限性。因为访问控制不在模型中实施,可能某些请求会绕过我们覆盖的视图,从而绕过我们打算实施的访问控制。只要在显示此组件内容的每个视图中正确应用访问控制,安全性应该没问题。但是,总是有可能引入一些我们没有覆盖其显示的新模块。或者,也许由于人为错误,在要编辑的许多视图中访问控制实施不一致。如果安全性非常重要,一个人可能会考虑一个更技术性的面向对象的方法来覆盖模型。
我的经验
解释我如何为这个客户实施访问控制超出了我的范围。此外,必须注意每个项目都是不同的,需要仔细检查。当然,一个人可以在实施中识别一些解决方案模式,我将在下面分享一些。
在这个项目中,第一步是为每个自定义分类和每个条目添加访问级别。我在自定义分类和条目的数据库表中添加了一个“访问”字段。该字段的名称、类型和属性与核心组件中的“访问”字段匹配(int类型,大小为10,无符号)。在管理端,我覆盖了每个(分类和条目)的编辑屏幕,以便添加访问级别的下拉列表代码。这个输入字段与条目的数据库字段“访问”相链接。到这一点,每个分类和每个条目现在都包括一个在其编辑表单中设置的访问级别。
在管理端,我覆盖了显示分类和显示条目的视图。在HTML表中添加了一个新列,以便显示访问级别。
我将访问控制的实际工作编码到自己的自定义库类中。根据需要,我向这个类添加了所需的公共函数。完成项目时,我实现了以下函数的接口
filterToAccessibleCourses($courses=array() ) // reduces the array to just the accessible items filterToAccessibleCategories($cats=array() ) canAccessCourse($item, $cat=null) // returns true if current user can access this item canAccessCategory($cat) getAccessLevelName($id) // id of the access level
将我的重点转向网站的首页,我检查了组件使用的十几个视图文件。我从生成分类列表的视图开始。这个视图文件接受一个包含所有分类的数组。如果组件通过模型实现访问控制,则这个数组的内容已经过滤,只包含用户应该能够看到的项目。但由于开发者没有提供访问控制,因此我们的覆盖视图负责过滤列表。必须覆盖处理列表的每个视图,以便我们可以通过调用自定义库中的相应过滤函数来过滤数组
$courses = $accessMgr->filterToAccessibleCourses($courses); $categories = $accessMgr->filterToAccessibleCategories ($categories);
一些视图显示特定分类或条目的详细信息。如果模型处理访问控制,则视图不会向未经授权查看的用户发送内容以显示。但情况并非如此,并且可能有多个路径通向分类或条目页面。因此,访问控制又成为视图的责任。解决方案是覆盖这些单条目视图,并在文件开始时添加一个授权检查:如果用户没有授权访问项目,则输出适当的消息并调用return;语句。这个return;语句停止此文件处理其内容。以下是一个示例代码
$accessMgr = new AccessManager(); // class from my custom library if( ! $accessMgr->canAccessCourse($this->program,$program->cat) ){ echo 'message that the user is not authorized to view this item'; return; }
应仔细检查每个视图和替代布局,以确定如何实现访问控制。假设您需要覆盖每个视图和替代布局 - 不仅针对组件,还针对访问此组件数据的任何模块。如果某些已安装的插件与组件相关联(例如,特定组件的搜索插件),您还需要审查这些插件。
当与尚未实现Joomla的ACL组件一起工作时,不应惊讶于发现一些非常规的方法,这些方法忽视了Joomla的覆盖功能。在多个视图中,我发现内容是由辅助文件处理的,并以完成后的HTML返回。问题是辅助文件侵占了视图格式化内容的能力,以及通过覆盖添加访问控制的机会。解决方案是将辅助类复制到我的自定义库中,重命名类以避免命名冲突,并在那里编码所需的访问控制。然后,覆盖输出文件引用这个新类而不是原始类。这不是一个完美的解决方案,但它是可行的 - 原始功能得到保留,同时我可以安全地添加访问控制。
添加自定义访问控制
这项练习概述了向组件添加访问控制的方法。原始组件实际上被扩展,使其核心可以升级,而不会丢失我们实施的更改。
此方法还可以用于注入由Joomla的标准ACL未处理的附加或更具体的规则。例如,我们可以添加逻辑,即作者只能发布/取消发布他自己的文章 - 这比开箱即用的控制级别更精细,但可以通过覆盖来实现。
这项额外工作确实需要一名熟练的开发者以及细致的注意力,但多亏了Joomla的MVC架构和覆盖功能,问题是可以解决的。当然,更好的情况是如果能找到一个提供Joomla的ACL的替代组件。
在Joomla社区杂志上发表的一些文章代表了作者对特定主题的个人观点或经验,可能并不与Joomla项目的官方立场一致。
通过接受,您将访问由https://magazine.joomla.net.cn/之外的第三方提供的服务。
评论 1
感谢这些真正棒的信息教程!希望看到更多关于Joomla 4的教程!