0

我正在使用 CakePHP 构建一个 Web 应用程序,它允许用户为他们的公司/组织创建子域。例如company-name.domain.com

为此,我有一个用户表和一个子站点表,如下所示:

用户:id、用户名、电子邮件、密码、subsite_id 子站点:id、名称、域

如您所见,用户链接到子站点,子站点可以通过这种关系拥有多个用户,但用户只能属于一个子站点。

我在 AppController 中使用以下内容检查子域是否有效:

function checkSubdomain()
{
    if ($_SERVER['HTTP_HOST'] != 'domain.com')
    {
        $domain_parts = explode('.',$_SERVER['HTTP_HOST']);

        if (count($domain_parts) != 3) exit('Invalid url');

        $subdomain = $domain_parts[0];

        $this->loadModel('Subsite');

        $subsites = $this->Subsite->find('all', array('conditions'=>array('domain'=>$subdomain)));

        if(empty($subsites))
        {
            exit('Subsite not found');
        }
    }
}

function beforeFilter()
{
    $this->checkSubdomain();
}

这基本上表示如果未找到子域,则会出错,这很好用!我遇到的问题是,出于明显的原因,我希望子域无法访问某些页面,例如主页、关于、定价和注册表单。

这样做的最佳方法是什么?无需检查我的所有控制器是否是子域?

我注意到 getballpark.com 等其他应用程序在注册过程中使用子域。它们是允许某些子域拥有某些页面的特殊方式吗?

谢谢

如果正在使用子域,则基于以下答案的附加要求会阻止对某些控制器的访问:

另一个问题是,如果用户登录,我使用 HomeController 来显示启动促销页面或应用程序的仪表板。问题是我怎么说你必须只登录该方法,如果你通过访问一个子域。或者他们是实现 sam 功能的完全更好的解决方案。

在保护注册(在我的 UsersController 中)时,我还会阻止登录并忘记密码方法。我如何在不打破 Cake 约定并将方法移动到单个控制器的情况下解决这个问题?干杯

4

2 回答 2

1

我建议制作一个禁止控制器列表,然后在应用控制器beforeFilter处理程序中检查它,以防当前请求来自子域:

protected $_forbiddenSubdomainControllers = array
(
    'about',
    'home',
    'pricing',
    'signup'
);

public function beforeFilter()
{
    parent::beforeFilter();

    if($this->checkSubdomain() &&
       in_array($this->request->params['controller'], $this->_forbiddenSubdomainControllers))
    {
        throw new ForbiddenException('This URL is inaccessible');
    }

}

public function checkSubdomain()
{
    if ($_SERVER['HTTP_HOST'] != 'domain.com')
    {
        $domain_parts = explode('.',$_SERVER['HTTP_HOST']);

        if (count($domain_parts) != 3) exit('Invalid url');

        $subdomain = $domain_parts[0];

        $this->loadModel('Subsite');

        $subsites = $this->Subsite->find('all', array('conditions'=>array('domain'=>$subdomain)));

        if(empty($subsites))
        {
            throw new NotFoundException('Subsite not found');
        }

        return true;
    }

    return false;
}

请注意,我已经更改了您的checkSubdomain方法,以便它返回truefalse取决于请求是否来自(有效)子域,并且我还更改了您的exit调用以引发错误,这是 CakePHP 处理此类情况的首选方式.

如果您想允许某些子域使用其中一些“特殊”控制器,那么我建议将允许的控制器存储在数据库中并将它们与Subsite模型相关联,然后您可以在检查中包含这些名称。


编辑 (05.11.2012)

仅在从子域请求时才要求对特定控件进行身份验证,例如可以通过使用AuthComponent::allow. 在Home控制器beforeFilter回调中,您可以检查子域,然后适当地允许/拒绝未经身份验证的访问。例如,如果请求不是来自子域,则允许所有操作:

public function beforeFilter()
{
    parent::beforeFilter();

    if(!$this->checkSubdomain())
    {
        $this->Auth->allow('*');
    }
}

考虑到您限制对特定操作的访问的新要求,我想说,根据您的应用程序的复杂性,限制/授予访问权限可能是ACL的一项工作。

相反,“手动”执行此操作,我的第一个示例可以通过操作进行扩展,例如:

protected $_denyAccessMap = array
(
    'about',
    'home',
    'pricing',
    'users' => array
    (
        'signup'
    )
);

public function beforeFilter()
{
    parent::beforeFilter();

    if($this->checkSubdomain())
    {
        $controller = $this->request->params['controller'];
        $action = $this->request->params['action'];

        if(in_array($controller, $this->_denyAccessMap) ||
          (array_key_exists($controller, $this->_denyAccessMap) && in_array($action, $this->_denyAccessMap[$controller])))
        {
            throw new ForbiddenException('This URL is inaccessible');
        }
    }
}

这将拒绝对控制器abouthomepricing、和users控制器signup操作的访问。

如前所述,这也可以使用 ACL 来完成,并且由于您说您需要授予某些子域更多的访问权限,这可能是更好的选择,它可以让您动态控制访问限制。看看Simple Acl 控制的应用程序教程,你可以很容易地使用它,你只需要用Group你的模型替换Subsite模型。

这样,您可以授予特定子站点以及与该子站点关联的用户对特定操作Subsite的访问权限,例如允许id 1访问除Users控制器subscribe方法之外的所有控制器:

$this->Subsite->id = 1;

$this->Acl->allow($this->Subsite, 'controllers');
$this->Acl->deny($this->Subsite, 'controllers/User/subscribe');

或者相反,阻止对Users控制器的访问,期望特定loginpasswordRecovery操作:

$this->Acl->allow($this->Subsite, 'controllers');
$this->Acl->deny($this->Subsite, 'controllers/User');
$this->Acl->allow($this->Subsite, 'controllers/User/login');
$this->Acl->allow($this->Subsite, 'controllers/User/passwordRecovery');

例如,检查是否允许访问可以在应用程序控制器beforeFilter回调中完成:

public function beforeFilter()
{
    parent::beforeFilter();

    $subsite = $this->checkSubdomain();
    if(!empty($subsite))
    {
        $aco = 'controllers/' . $this->name . '/' . $this->request->params['action'];
        if($this->Acl->check($subsite, $aco))
        {
            throw new ForbiddenException('This URL is inaccessible');
        }
    }
}

public function checkSubdomain()
{
    if ($_SERVER['HTTP_HOST'] != 'domain.com')
    {
        $domain_parts = explode('.',$_SERVER['HTTP_HOST']);

        if (count($domain_parts) != 3) exit('Invalid url');

        $subdomain = $domain_parts[0];

        $this->loadModel('Subsite');

        $subsite = $this->Subsite->find('first', array('conditions'=>array('domain'=>$subdomain)));

        if(empty($subsite))
        {
            throw new NotFoundException('Subsite not found');
        }

        return $subsite;
    }

    return false;
}

请注意,我已经更改了checkSubdomain方法,以便它返回find结果,或者false可以轻松地将其用于 ACL 检查。

于 2012-11-03T16:52:24.463 回答
0

除了主要的问题和答案之外,我还会考虑开发环境......因为几天前我遇到了类似的问题,我一直在研究它,这就是我所做的:

在 AppController 内部:

function checkAccess() {
    if (count($domain_parts) != 3 &&
       (count($domain_parts) == 2 && $domain_parts[1] != 'localhost:8080')) {
        throw new NotFoundException();
    }
}

内部 checkSubdomain() 方法:

checkSubdomain() {

    [...]

    $this->checkAccess();

    [...]

}

由于我在 localhost 上测试子域功能,我可以使用 xxx.localhost 和 yyy.localhost 并在部署之前检查一切是否正常。

于 2014-08-26T22:24:34.373 回答