我一直在研究Zend Framework 2应用程序。它有一个包装模型层的服务层。控制器调用服务层,该层与模型和映射器进行通信。
我发现的问题是访问控制需要付出很多努力,包括测试工作。该应用程序使用社交图,因此访问规则非常复杂。例如,如果用户A想要访问关于用户B的特定类别的信息,则他们可能需要是某种类型的用户,并且可能需要与B的特定数量的连接。
某些访问规则似乎属于模型层。要将它们放在服务层中,我必须将模型功能泄漏到服务中。其他人似乎属于服务层,例如,从映射器中提取对象集合的那些(映射器没有访问控制的概念)。
有没有人有过这种访问控制复杂性的经验?并不是系统不起作用,只是50%的代码(以及测试,模拟等)被访问控制所占用。有更简单的方法吗?
我考虑过驱动访问控制事件 - 每个安全方法都会触发一个事件,如果需要,该事件的监听器会抛出异常。 (我不会在测试时附加听众,所以我节省了大约一半的测试用例)。有没有人有这方面的经验?
我很久以前尝试过ACL和RBAC,但找不到满意的解决方案。大约一年前我研究过XACML,但它看起来有些过分,即便如此,我也不确定它会解决这些问题。
答案 0 :(得分:1)
由于我是面向方面的PHP编程专家,我认为您应该看一下这个范例,因为它是由Xerox PARC设计的,用于解决面向对象代码中的交叉问题。
关于访问控制的问题描述了典型的分散功能,你认为这个代码在任何地方都是重复的,这是正确的,因为传统的面向对象范式并不能解决如何优雅地做到这一点。
OOP的可能解决方案是创建可以检查访问权限的装饰器。另一种方法是将类的每个API方法修改为trigger an event,并准备一个安全处理程序,在事件分派期间检查访问权限。两种解决方案都不完美。
在JAVA世界中,AOP已被用于访问控制很长时间(例如,使用AspectJ,Spring)。我开发了Go!用于PHP的AOP框架,它是Spring框架的移植版本,由AOP Alliance接口提供支持。
让我们看一下如何用AOP做到这一点。主类包含几个具有域逻辑的业务方法:
class MyService {
public function updateProfile() {...}
public function deleteProfile() {...}
}
然后我们使用doctrine annotation @Secured(PROFILE_EDIT)
标记我们的方法,并定义一个方面,它将在源代码中的任何地方用这个注释拦截方法的执行:
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Before;
/**
* Security aspect
*/
class SecurityAspect implements Aspect
{
/**
* This advice intercepts an execution of secured methods
*
* Logic is pretty simple: we check an access rights before original method and
* then invoke original method or throw an exception.
*
* @param MethodInvocation $invocation Invocation
*
* @Before("@annotation(Demo\Annotation\Secured)")
*/
public function beforeSecuredMethod(MethodInvocation $invocation)
{
$right = $invocation->getMethod()->getAnnotation('Demo\Annotation\Secured')->value;
if (!Security::isGranted($right)) {
throw new AuthorizationException("Access denied. {$right} is required.");
}
}
}
您可以阅读有关logging with AOP的更详细的文章,了解如何实施授权。我还有working demo of logging aspect for Zend Framework2来检查效果和more examples。