这正是我们最近在设置 AWS Elastic Beanstalk 时遇到的问题。问题是,当负载均衡器访问您的实例时,它实际上使用 PORT 80 (http),如果您密切关注流程:
- 你
AuthComponent
用来$controller->redirect()
做 loginRedirect [ AuthComponent::419 ]
redirect()
用于Router::url($loginRedirect, true)
获取完整的 url。[控制器::774 ]
Router::url()
用于Router::fullBaseUrl()
解析完整的基本 url [ Router::899 ]
Router::fullBaseUrl()
用于Configure::read('App.fullBaseUrl')
读取写入的完整基本 url。[路由器::929 ]
那么问题来了,谁解决了fullBaseUrl
?
进一步挖掘,你会看到Cake\bootstrap.php
,[罪魁祸首在该文件的第 158 行
if (!defined('FULL_BASE_URL')) {
$s = null;
if (env('HTTPS')) { <---------------- @@@@ env('HTTPS') is false!!!! @@@@
$s = 's';
}
$httpHost = env('HTTP_HOST');
if (isset($httpHost)) {
define('FULL_BASE_URL', 'http' . $s . '://' . $httpHost);
Configure::write('App.fullBaseUrl', FULL_BASE_URL);
}
unset($httpHost, $s);
}
知道了?AWS 负载均衡器会在端口 80 上访问您的实例,而不会告诉您这是一个 https 调用(以通常的方式),因此 cake 被误认为它是 http,因此会导致重定向循环。
我们的解决方案相当老套。由于我们使用的是弹性 beanstalk 并且我们可以编辑环境变量,我们在我们的.config
文件中执行此操作.ebextensions
option_settings:
- option_name: HTTPS
value: 1
当我们根本不想要 http(并且总是强制重定向使用 https)时,这很好。但更好的是,也许使用HTTP_X_FORWARDED_*
变量来确定环境(如果你仍然需要http):
如果你debug($_SERVER)
,你应该能够看到这样的东西:
["HTTP_X_FORWARDED_FOR"]=>"xx.xx.xx.xx, xx.xx.xx.xx, ...." //note that the first is IP, the rest are proxies
["HTTP_X_FORWARDED_PORT"]=>"443"
["HTTP_X_FORWARDED_PROTO"]=>"https"
所以这让你只有一个选择:编辑你的bootstrap.php
并在顶部添加这一行:
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
一旦它尝试解析完整的基本 URL,您将获得正确的协议集。
(或者,define('FULL_BASE_URL'..)
如果你愿意,你可以,但我不喜欢它:p)
笔记
当我们第一次进入这个负载平衡世界时,一切都略有不同。如果您的代码的任何部分没有表现出应有的行为,请始终检查$_SERVER
数组。例如,你不应该再依赖$_SERVER['REMOTE_ADDR']
了。您需要从中分解并检测此变量$_SERVER['HTTP_X_FORWARDED_FOR']
。
编辑
如果安全是一个问题,请查看此线程。上面的黑客可能很方便,但不是防弹:https ://github.com/cakephp/cakephp/issues/2035
2013 年 9 月 15 日基于 CakePHP 2.4.1 的回答,提交085636ea1bb2a57b084456e776bfada01dee71df