问题
Due to the way we've setup our AWS hosting, I have to use absolute URLs for CakePHP's loginAction. Currently, my loginAction looks like this (doesn't work now)
'loginAction' => array('controller' => 'users', 'action' => 'login'),
Combine that with a redirect from HTTP -> HTTPS I get a redirect loop. I can prove this by setting an absolute URL like this (works fine)
'loginAction' => array('https://foo.com'),
I thought I could just set the final param to true like this
'loginAction' => array('controller' => 'users', 'action' => 'login', TRUE),
but that doesn't work either.
My final failure I tried setting something like this,
'loginAction' => "https://{$_SERVER['HTTP_HOST']}/users/login",
Which throws a totally unrelated error - PHP Parse error: syntax error, unexpected '"'
So my questions are,
- Can I set an absolute URL in loginAction()
- Or, can I tell Cake to always use an absolute URL somewhere like, routes.php or bootstrap.php?
I've seen people talking about FULL_BASE_URL and other constants, none seem to help.
回答1:
This is our exact problem that we faced lately, when we set up AWS Elastic Beanstalk. The problem is that, when load balancer hits up your instance, it actually uses PORT 80 (http), and if you follow the flow closely:
- Your
AuthComponentuses$controller->redirect()to do the loginRedirect [AuthComponent::419] redirect()usesRouter::url($loginRedirect, true)to get the full url. [Controller::774]Router::url()usesRouter::fullBaseUrl()to resolve full base url [Router::899]Router::fullBaseUrl()usesConfigure::read('App.fullBaseUrl')to read the written full base url. [Router::929]
So the question is, who resolved fullBaseUrl?
Further digging it down, you will see that in Cake\bootstrap.php, [the culprit is in line 158 of that file
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);
}
Got it? AWS Load balancer hits your instance on port 80 without telling you it's a https call (in the usual way), so cake is being fooled that it's http, and hence it results in redirection loops.
Our solution is rather hacky. Since we are using elastic beanstalk and we can edit environment variables, we do this in our .config file in the .ebextensions
option_settings:
- option_name: HTTPS
value: 1
This is good when we don't want http at all (and always force redirection uses https). But what's better is, perhaps use the HTTP_X_FORWARDED_* variables to determine the environment (if you still need http):
If you debug($_SERVER), you should be able to see something like this:
["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"
So this left you only one choice: Edit your bootstrap.php and add in this line on the top:
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
And once it tries to resolve full base url, you will get the correct protocol set.
(Or, you could define('FULL_BASE_URL'..) if you want, but I don't like it :p)
Note
When we first entering this load balancing world, everything works slightly different. If any part of your code is not behaving like it should, always check the $_SERVER array. For example, you shouldn't rely on $_SERVER['REMOTE_ADDR'] anymore. You need to explode and detect this variable from $_SERVER['HTTP_X_FORWARDED_FOR'] instead.
EDIT
Check out this thread if security is a concern. The above hack could be handy, but not bullet proof: https://github.com/cakephp/cakephp/issues/2035
Answer based on CakePHP 2.4.1 on September 15, 2013, commit 085636ea1bb2a57b084456e776bfada01dee71df
来源:https://stackoverflow.com/questions/18805437/cakephp-absolute-urls-for-loginaction