CakePHP 2.3.2 BasicAuthentication 不起作用
Posted
技术标签:
【中文标题】CakePHP 2.3.2 BasicAuthentication 不起作用【英文标题】:CakePHP 2.3.2 BasicAuthentication not working 【发布时间】:2013-04-08 23:44:09 【问题描述】:我试用了位于http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.html 的“简单 Acl 控制的应用程序 1&2”教程。
完成此操作后,我尝试激活 BasicAuth 而不是 FormAuth。
我在我的 UsersController 中重新实现了 login() 函数,如下所示:
public function login()
if ($this->Auth->login())
return $this->redirect($this->Auth->redirect());
else
$this->Session->setFlash('Not able to login');
并将我的 AppController 中的 $components 变量更改为以下内容:
public $components = array(
'Acl',
'Auth' => array(
'authorize' => array(
'Actions' => array('actionPath' => 'controllers')
),
'authenticate' => array('Basic')
),
'DebugKit.Toolbar',
'Session'
);
BasicAuth“弹出窗口”按预期显示,但当我尝试登录时,它会在无限循环中重新出现。除了包含 DebugKit 之外,我在完成教程后没有更改任何内容。
我错过了什么?我希望有人可以帮助我,因为我想用 CakePHP 编写我的下一个项目!
更新
应用控制器
public function beforeFilter()
//Configure AuthComponent
$this->Auth->allow('display');
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
$this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add');
用户控制器
public function beforeFilter()
parent::beforeFilter();
我正在尝试访问例如/users/
使用教程中描述的 FormAuth 就像一个魅力,所以不会有权限问题。登录数据对于测试来说非常简单(管理员:管理员),所以应该也没有问题。
更新 2
在我的 Apache 日志中,我得到以下信息,所以它说我没有被授权:
IP - - [16/Apr/2013:18:08:37 +0200] "GET /users/login HTTP/1.0" 401 5179 "-" "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv :23.0) 壁虎/20130414 火狐/23.0"
更新 3
似乎出于某种原因,用户和密码要么未发送,要么未保存在 PHP 中。如果我将/lif/Cake/Controller/Auth/BasicAuthenticate
重写为以下内容,它可以工作!
public function authenticate(CakeRequest $request, CakeResponse $response)
$_SERVER['PHP_AUTH_USER'] = $_SERVER['PHP_AUTH_PW'] = "admin";
$result = $this->getUser($request);
if (empty($result))
$response->header($this->loginHeaders());
$response->statusCode(401);
$response->send();
return false;
return $result;
更新 4
不知道这是否有帮助,但服务器正在运行 Plesk 11,最新更新,没有特殊修改。
更新 5
好的,“thaJeztah”的答案很有用,但现在我遇到了更多可以细分的问题。
将模式从 fcgid 更改为 apache 模块
1.1。结果可以登录,但注销不起作用!重定向后,会话似乎被清除,但我仍然可以访问每个受限页面,直到我清除浏览器中的“活动登录”,因为它在 Firefox 中被调用。
var_dump($this->Session->read('Auth.User')); NULL
当我访问 /users/login 时,我会自动登录并重定向,而无需输入登录凭据。
print "<pre>"; print_r($this->Session->read('Auth.User')); print "</pre>"; Array ( [id] => 1 [username] => admin [group_id] => 1 [created] => 2013-04-12 12:54:26 [modified] => 2013-04-16 14:27:24 [is_active] => 1 [Group] => Array ( [id] => 1 [name] => Admin [created] => 2013-04-12 12:46:42 [modified] => 2013-04-12 12:46:42 ) )
使用基于 .htaccess 的解决方案也可以,它甚至看起来好像这是唯一需要的更改(我删除了 list() 代码,因为我从未进入它并且它也可以正常工作)。
2.1。与上述相同的问题,无法真正注销。
更新 6
可能是我最后一次或最后一次更新。 :-)
现在我正在尝试通过将用户登录为我创建的只能访问 /users/login
和 /pages/home
的访客用户来进行“假注销”:http://guest:guest@my.domain/users/login
访问/users/logout
也可能有效,因为我在那里使用了这段代码:
public function logout()
$user = $this->User->find('first', array('conditions' => array('username' => 'guest')));
$this->Auth->login($user['User']['id']);
我简直不相信,这将是一致的,因为我相信会话数据将在一段时间内被删除,并且浏览器仍然获得有效的管理员登录并使用这些进行身份验证 - 我是对的吗?
之后,我可以使用http://admin:admin@my.domain/users/login
再次登录其他用户。不完美,但至少适用于 Firefox。
所以基本上最后一个问题:关于如何在访问/users/login
时强制使用 BasicAuth 有什么建议吗?这样我就可以随时使用任何客户端轻松切换用户。
更新 7
我找到了一种方法来完全按照我接受的答案中的想法做到这一点。我希望我抓住了所有的边缘情况,如果没有,请随时纠正我!
(Ps:当使用 ACL 和/或基本身份验证时,至少 AppController 中的 isAuthorized() 似乎被忽略(它已被识别,但没有效果 - 当我在不更改 $components 的情况下删除该方法时,出现错误) 这导致我在不使用 isAuthorized() 的情况下实现了这一点。)
AppController.php
public function beforeFilter($redirectlogin = true)
//Configure AuthComponent
$this->Auth->allow('display', '/users/login');
$this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
$this->Auth->logoutRedirect = array('controller' => 'pages', 'action' => 'home');
$this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'home');
$this->Auth->unauthorizedRedirect = array('controller' => 'HTTPCODE', 'action' => 'c403');
if($redirectlogin && $this->Session->read('Auth.needs_reauthenticate'))
if(!($this->request->params['controller'] == $this->Auth->loginRedirect['controller'] && $this->request->params['pass'][0] == $this->Auth->loginRedirect['action']))
$this->redirect('/users/login');
UsersController.php
public function beforeFilter()
parent::beforeFilter(false);
public function login()
$this->autoRender = false;
$this->Session->write('Auth.needs_reauthenticate', true);
if(!$this->Session->check('Auth.count'))
$count = 1;
else
$count = $this->Session->read('Auth.count') + 1;
$this->Session->write('Auth.count', $count);
if($this->Session->read('Auth.needs_reauthenticate'))
if((isset($_SERVER['HTTP_AUTHORIZATION']) && $this->Session->read('Auth.count') == 1) || (!isset($_SERVER['HTTP_AUTHORIZATION']) || empty($_SERVER['HTTP_AUTHORIZATION']) || !$this->Session->check('Auth.sent_header_step') || $this->Session->read('Auth.sent_header_step') < 1))
unset($_SERVER['HTTP_AUTHORIZATION']);
$this->Session->write('Auth.redirectTo', $this->Auth->redirect());
$this->response->header(sprintf('WWW-Authenticate: Basic realm="%s"', env('SERVER_NAME')));
$this->response->statusCode(401);
$this->response->send();
$this->Session->write('Auth.sent_header_step', 1);
if(isset($_SERVER['HTTP_AUTHORIZATION']))
$this->Session->write('Auth.sent_header_step', 0);
$base64string = base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6));
if(!(strlen($base64string) > 1 && substr($base64string, -1, 1) != ":"))
$_SERVER['PHP_AUTH_USER'] = "";
$_SERVER['PHP_AUTH_PW'] = "";
$data = true;
$this->Auth->logout();
if(isset($data) && $this->Session->read('Auth.count') > 1)
if($this->Auth->login())
$this->Session->write('Auth.needs_reauthenticate', false);
if($this->Session->check('Auth.redirectTo'))
$redirectTo = $this->Session->read('Auth.redirectTo');
$this->Session->delete('Auth.redirectTo');
$this->Session->delete('Auth.count');
return $this->redirect($redirectTo);
else
return $this->redirect($this->Auth->redirect());
else
$this->response->statusCode(403);
// my 403 message
else
if(!isset($_SERVER['HTTP_AUTHORIZATION']) && $this->Session->read('Auth.count') > 1 && isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && trim($_SERVER['PHP_AUTH_USER']) != "" && trim($_SERVER['PHP_AUTH_PW']) != "")
if($this->Auth->login())
$this->Session->write('Auth.needs_reauthenticate', false);
if($this->Session->check('Auth.redirectTo'))
$redirectTo = $this->Session->read('Auth.redirectTo');
$this->Session->delete('Auth.redirectTo');
$this->Session->delete('Auth.count');
unset($_SERVER['HTTP_AUTHORIZATION']);
unset($_SERVER['PHP_AUTH_USER']);
unset($_SERVER['PHP_AUTH_PW']);
return $this->redirect($redirectTo);
else
return $this->redirect($this->Auth->redirect());
else
$this->response->statusCode(403);
// my 403 message
$this->response->statusCode(403);
// my 403 message
提前致谢
阿德里安
【问题讨论】:
你是否在 beforeFilter 中添加了函数(就像在教程中一样)?特别是$this->Auth->loginRedirect
。您似乎已登录,但重定向无法正常工作。如果您有该代码,您可以用它更新您的问题吗?并检查登录的用户是否真的有权访问该操作?
@nuns:用请求的信息更新了我的帖子
您使用的是 cakephp 2.3,尝试在登录函数中将 return $this->redirect($this->Auth->redirect());
更改为 return $this->redirect($this->Auth->redirectUrl());
(并将 BasicAuthenticate 恢复为原始状态)。如果这不起作用,因为覆盖对您有用,我建议扩展 BasicAuthenticate 类并更改 getUser() 函数。我见过有类似问题的couple of post,这似乎是解决方案。
再想一想......如果你必须手动设置环境变量PHP_AUTH_USER
和PHP_AUTH_PW
,这意味着它们之前没有设置(duh)。你在哪里设置它们?你调用 http rquest 的代码是什么?
@nuns 根据 thaJeztah 的回答更新了我的帖子,其中包含了这个想法(更新 5)
【参考方案1】:
将 PHP 作为(快速)CGI 运行时使用基本身份验证
您的网站可能配置为将 PHP 作为(快速)CGI 运行,在这种情况下 PHP_AUTH_USER
和 PHP_AUTH_PWD
键不存在于 $_SERVER
变量中。 BasicAuthenticate AuthComponent 依赖于这些键。
要么更改 Plesk 中的域/webhosting 设置以将 php 作为此网站/域的“apache 模块”运行,要么扩展 BasicAuthenticate 组件以通过其他方式获取这些变量。
关于这个主题的更多信息可以在这个问题中找到:
PHP_AUTH_USER not set?
对于 Symfony 框架,似乎有人编写了一个解决方法,在这种情况下也可能有用;
https://github.com/symfony/symfony/issues/1813
更新:使用基本身份验证时注销
使用basic authentication
时注销是不可能的。基本身份验证是一种“无状态”身份验证机制,这基本上意味着浏览器随每个请求发送用户凭据。换一种说法; 服务器不保持“状态”,浏览器会。使用基本身份验证,您需要浏览器发送用户凭据,并且只要浏览器发送有效凭据,您就允许浏览器访问受保护的页面。
退出的唯一方法是关闭浏览器,或者告诉浏览器关闭活动会话/登录。
在此处阅读更多信息:
http://en.wikipedia.org/wiki/Basic_access_authentication
http basic authentication "log out"
注意事项
Base Authentication 不是一种安全的身份验证机制; 每个请求都会将用户名和密码发送到服务器。发送的密码未加密(仅 base64 编码以防止特殊字符出现问题)。
虽然表单身份验证也会发送未加密的密码,但它(更安全一点)因为它只会在登录时发送用户名/密码。后续请求只会发送 Session-id,它可以设置为过期和仅限于特定的 IP 和/或浏览器类型。
在所有情况下,通过 SSL 保护连接显然很重要。
在登录页面强制重新认证
这只是“大声思考”,未经测试且高度实验性:)
试试这个;
如果没有会话处于活动状态,则按正常方式进行。在基本身份验证中,无法区分“已登录”用户和“新用户”——它是无状态的
如果会话处于活动状态,则用户显然正在进行活动会话。 不要破坏会话,而是改变规则;
如果发送的凭据与会话中的用户名相同 用户(或者,更好:$this->Auth->user('username');
?,那么 invalidate 会话(不是 destroy) 并通过发送登录标头强制用户重新进行身份验证;
您可以从 BasicAuthenticate 行为中复制标头;在此处查看源代码BasicAuthenticate::authenticate()
关于“复制标题”;也许扩展 BasicAuthenticate 是一种更清洁的方法;处理自定义版本中的所有自定义代码。
此外,检查会话在AppController::isAuthorized()
中是否仍然“有效”(参见Using ControllerAuthorize)
类似这样的东西(样机代码):
登录页面/操作:
if ("usercredentials sent by browser" === "current logged in user in session")
// Mark session as 'needs-to-reauthenticate'
$this->Session->write('Auth.needs_reauthenticate', true);
// Need to find a clean approach to get the BasicAuth loginHeaders()
// *including* the right settings (realm)
$this->response->header(/*BasicAuth::loginHeaders()*/);
// Access denied status
$this->response->statusCode(401);
return $this->response->send();
AppController::isAuthorized()
if ($this->Session->read('Auth.needs_reauthenticate'))
return false;
else
// Normal 'isAuthorized()' checks here
注意: 一旦浏览器在活动会话期间访问了“登录”页面,用户要么必须使用不同凭据登录,要么关闭浏览器以再次登录.
如果在关闭并重新打开浏览器后会话 cookie 仍然存在,这可能会出现问题。尝试强制 session-cookie 成为 "real" session-cookie,并通过将 Session.cookieTimeout
设置为 0
将其在浏览器关闭时删除(请参阅 Session Configuration
【讨论】:
@boindil 在我的回答中添加了一些信息来解释“基本身份验证”机制 非常感谢,这对我帮助很大!我需要这个,因为我正在编写一个 REST API 并试图使其以某种方式安全和灵活。但是我需要使用 BasicAuth,因为这是一个指导原则(API 本身是重写的,并且有多个客户端不能轻易更改)。是的,当然会使用 SSL。更新了我的帖子(更新 6)。 @boindil 我做了一些思考,将其添加到我的答案中。未经测试,只是大声思考:D 再次感谢您的所有帮助,我将适用于我的解决方案编辑到更新 7 中。 很高兴你能成功!为了改进您的代码,我建议将一些逻辑移出您的控制器并(例如)移到自定义身份验证对象(请参阅Creating Custom Authentication objects)。但是,这不是这个问题的范围。再次,太好了,你成功了!以上是关于CakePHP 2.3.2 BasicAuthentication 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
php [cakephp:Paginator示例] CakePHP上PaginatorComponent的示例代码。 #cakephp
php [cakephp:mysqldump] mysqldumpのcakephp実装サンプル。#php #cakephp #mysql
使用 Cakephp-jwt-auth [CakePHP 3] 过期后颁发新令牌