通过覆盖添加访问控制
如果想要使用的组件没有实现您需要的 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 的内容!