繁体   English   中英

使用CakePHP构建具有子域的Web应用

[英]Using CakePHP to build web apps with subdomains

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

为此,我有一个Users表和一个Subsites表,如下所示:

用户: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约定并将方法移至单个控制器? 干杯

我建议列出禁止的控制器列表,然后在应用程序控制器的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身份验证,例如,可以使用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控制的Application教程 ,您可以轻松地使用它,只需将Group模型替换为Subsite模型。

这样,您可以授予特定的子站点访问权限,并因此授予与该子站点相关联的用户访问特定操作的权限,例如,允许id 1Subsite访问所有控制器,但“ 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检查。

除了主要的问题和答案外,我还将考虑开发环境...由于几天前我遇到了类似的问题,因此我进行了研究,这是我的工作:

在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并在部署之前检查一切正常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM