CakePHP 2.3.2 BasicAuthentication无效

时间:2013-04-16 14:05:25

标签: cakephp basic-authentication cakephp-2.3

我尝试了位于http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.html的“Simple 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编写我的下一个项目!

更新

AppController的

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');
}

UsersController

public function beforeFilter() {
    parent::beforeFilter();
}

我正在尝试访问,例如/users/使用本教程中描述的FormAuth,就像魅力一样,因此不存在权限问题。 Logindata非常简单,可以测试(admin:admin),所以也没有问题。

更新2

在我的Apache Log中,我得到以下信息,因此它说我没有被授权:

  

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)Gecko / 20130414 Firefox / 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”的答案很有用,但现在我遇到了更多可以细分的问题。

  1. 将模式从fcgid更改为apache模块

    1.1。工作登录的结果,但注销不起作用!重定向后,会话似乎被清除,但我仍然可以访问每个受限制的页面,直到我清除浏览器“Active Logins”,因为它在Firefox中被调用。

  2. 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
            )
    
    )
    
    1. 使用基于.htaccess的解决方案也可以工作,它甚至看起来好像这是唯一需要的更改(我删除了list()代码,因为我从未进入它并且它也能正常工作)。 / p>

      2.1。与上述问题相同,无法真正注销。

    2. 更新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
                  }
              }
          }
      

      提前致谢

      阿德里安

1 个答案:

答案 0 :(得分:3)

将PHP作为(快速)CGI运行时使用基本身份验证

您的网站可能配置为将PHP作为(快速)CGI运行,在这种情况下,PHP_AUTH_USERPHP_AUTH_PWD键不存在于$_SERVER变量中。 BasicAuthenticate AuthComponent依赖于这些键。

更改Plesk中的域/网站托管设置,将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"

注释

基本身份验证不是安全的身份验证机制;用户名和密码将通过每个请求发送到服务器。密码以未加密发送(仅限base64编码,以防止出现特殊字符问题)。

虽然表单身份验证还会发送未加密的密码,但它更安全(因为它只会在登录时发送用户名/密码)。后续请求只会发送会话ID,可以设置为过期,限于特定的IP和/或浏览器类型。

在所有情况下,通过SSL保护连接显然非常重要。

强制在登录页面重新进行身份验证

这只是'大声思考',未经测试且极具实验性:)

试试这个;

  1. 如果没有会话处于活动状态,请按正常方式进行。在基本身份验证中无法区分“已登录”的用户和“新用户” - 它是无状态的

  2. 如果会话 处于活动状态,则用户显然正在进行活动会话。 不要销毁会话,但要改变规则;

    • 如果发送的凭据是相同的用户,则是会话内的用户名(或者,更好:$this->Auth->user('username'); ?,然后使会话无效 destroy )并强制用户通过发送登录标题重新进行身份验证;
    • 您可以从BasicAuthenticate行为中复制标题;在这里查看来源BasicAuthenticate::authenticate()
    • 关于'复制标题';也许扩展BasicAuthenticate是一种更清洁的方法;处理自定义版本中的所有自定义代码。
  3. 此外,检查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仍然存在,则此可能会出现问题。尝试强制会话cookie成为“真实”会话cookie,并通过将Session.cookieTimeout设置为0将其删除到浏览器上(请参阅Session Configuration