我正在使用CakePHP构建一个Web应用程序,允许用户为其公司/组织创建子域。例如company-name.domain.com
为实现这一点,我有一个Users表和一个Subsites表,如下所示:
用户:id,username,email,password,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();
}
这基本上说如果找不到子域,那么就出错了,这很好用!我遇到的问题是,由于显而易见的原因,我希望子域无法访问某些页面,例如home,about,pricing和注册表单。
这样做的最佳方法是什么?无需检查所有控制器,如果它是子域,还是没有?
我注意到getballpark.com等其他应用程序使用子域进行注册过程。它们是允许某些子域名具有特定页面的特殊方式吗?
由于
基于以下答案的附加要求,如果正在使用子域,则阻止访问某些控制器:
另一个问题是,如果用户登录,我使用HomeController显示启动促销页面或应用程序的仪表板。问题是我怎么说如果你只能登录该方法正在通过子域访问。或者它们是实现山姆功能的完全更好的解决方案。
在保护注册(在我的UsersController中)我也会阻止登录和忘记密码方法。如何在不必破坏Cake约定并将方法移动到单个控制器的情况下解决这个问题?干杯
答案 0 :(得分:1)
我建议制作一个禁止控制器的列表,然后在app controllers 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
方法,以便返回true
或false
,这取决于请求是否来自(有效)子域,并且我也已更改你的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');
}
}
}
这将拒绝访问控制器about
,home
和pricing
以及users
控制器signup
操作。
正如已经提到的,这也可以使用ACL完成,并且由于您说您需要授予某些子域更多的接受,这可能是更好的选择,它将允许您动态控制访问限制。看看Simple Acl controlled Application tutorial,您可以轻松使用此功能,您只需要将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
控制器,期望特定的login
和passwordRecovery
操作:
$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');
例如,可以在app控制器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检查。
答案 1 :(得分: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,并在部署之前检查一切正常。