CakePHP 3.8 身份验证插件:FAILURE_CREDENTIALS_MISSING

CakePHP 3.8 Authentication Plugin: FAILURE_CREDENTIALS_MISSING

这是我第一次尝试在 Cake 3.8 中使用身份验证插件。我遵循了 https://book.cakephp.org/authentication/1/en/index.html.

中概述的示例

Angular / Typescript 向 Cake 发送凭据的代码:

  /**
   * Login
   */
  public login( username : string,
                password : string,
                captchaResponse : string ) {

    return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                                {
                                  username,
                                  password,
                                  captchaResponse
                                },
                                {
                                  headers : new HttpHeaders()
                                    .set( 'X-Requested-With', 'XMLHttpRequest' )
                                }
    ).pipe( map( response => {
      // Store user details and jwt token in local storage
      // to keep user logged in between page refreshes
      this.user = response.data;
      this.setUser( this.user.profile );
      this.setToken( this.user.token );
      return this.user;
    } ) );
  }

Application.php

public function bootstrap() {
    // Authentication
    $this->addPlugin( 'Authentication' );
}

/**
 * Returns a service provider instance.
 *
 * @param \Psr\Http\Message\ServerRequestInterface $request Request
 * @param \Psr\Http\Message\ResponseInterface $response Response
 * @return \Authentication\AuthenticationServiceInterface
 */
public function getAuthenticationService( ServerRequestInterface $request, ResponseInterface $response ) {
    $service = new AuthenticationService();

    // Load identifiers
    $service->loadIdentifier( 'Authentication.Password', [
        'fields'   => [
            'username' => 'username',
            'password' => 'password',
        ],
        'resolver' => [
            'className' => 'Authentication.Orm',
            'finder'    => 'active',
            'userModel' => 'Users',
        ],
    ] );
    // Load the authenticators
    $service->loadAuthenticator( 'Authentication.Form', [
        'fields' => [
            'username' => 'username',
            'password' => 'password',
        ],
        'loginUrl' => '/users/token.json'
    ] );
    $service->loadIdentifier( 'Authentication.JwtSubject' );

    // Configure the service. (see below for more details)
    return $service;
}

public function middleware( $middlewareQueue ) {
    $middlewareQueue
        // Authentication Middleware
        ->add( new AuthenticationMiddleware( $this ) );

    return $middlewareQueue;
}

AppController.php

// Authentication Component
$this->loadComponent( 'Authentication.Authentication' );

我正在从 Angular 7 应用程序向 Cake(充当 REST API)发送凭据。信用通过嵌入在请求正文中的 post 请求发送。当我通过 $result->isValid().

检查时,我一直收到无效的身份验证结果

UsersController.php中,当我尝试跟踪错误时:

$this->log( $this->request->getData(), 'debug' );
$result = $result = $this->Authentication->getResult();
$this->log( $result, 'debug' );

我得到以下输出:

2019-12-03 07:35:30 Debug: Array
(
    [username] => myuser
    [password] => mypassword
    [captchaResponse] => 
)

2019-12-03 07:35:30 Debug: Authentication\Authenticator\Result Object
(
    [_status:protected] => FAILURE_CREDENTIALS_MISSING
    [_data:protected] => 
    [_errors:protected] => Array
        (
            [0] => Login credentials not found
        )

)

我只是想不通为什么 Cake 无法检测到 post 数据中是否存在凭据。还有其他人遇到同样的问题并有解决方案吗?

谢谢。

终于找到问题了。正如 Greg Schmidt 所建议的那样,在 FormAuthenticator::_getData() 中添加一些调试步骤有助于解决问题。我发现 _getData() 将空数组传递给验证器。

如果您查看上面的 Angular 代码,我直接将用户名和密码作为动态创建的 object 的一部分包含在 body:

return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                            // NOT THE RIGHT WAY OF DOING IT
                            {
                              username,
                              password,
                              captchaResponse
                            },
                            {
                              headers : new HttpHeaders()
                                .set( 'X-Requested-With', 'XMLHttpRequest' )
                            }
)

出于某种原因,新的 FormAuthenticator / Authetication 插件无法解析此信息 - 尽管 这不是旧 AuthComponent 的问题

相反,我不得不修改 Angular 代码以利用 FormData() object 自动添加 Content-Type(application/x-www-form-urlencoded 或表单数据)header 和内容边界。修改后的代码如下:

// Prepare form data
const formData = new FormData();
formData.append( 'username', username );
formData.append( 'password', password );
formData.append( 'captcha', captchaResponse );

return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                            formData,
                            {
                              headers : new HttpHeaders()
                                .set( 'X-Requested-With', 'XMLHttpRequest' )
                                .set( 'Accept', 'application/json' )
                                .set( 'Cache-Control', 'no-cache' )
                            }
)

希望这对以后遇到同样问题的人有所帮助。

在以非表单数据格式发送数据时,即非application/x-www-form-urlencoded,例如JSON(AFAICT是 Angular 的 HTTP 客户端的原始对象的默认值),你需要实现某种机制,将数据解码成 CakePHP 端的相关代码可以 read/understand,默认情况下 PHP 只解析表单数据。

使用旧的 auth 组件它对你有用,因为你很可能正在使用请求处理程序组件,默认情况下它支持自动将 JSON 请求数据解码为常规数组样式 post 数据可以从请求对象中检索 ($request->getData()).

然而,新的身份验证插件在中间件​​级别运行身份验证,即在涉及任何控制器(以及组件)之前,因此身份验证中间件将无法访问解码数据,请求对象上的数据将为空(原始 JSON 字符串可以通过 $request->input() 获得)。

为了让它工作,引入了主体解析器中间件,它可以做请求处理程序组件所做的事情,即解析原始输入数据,并用它填充常规请求数据。您将它放在身份验证中间件之前的队列中,禁用请求处理程序组件输入解码,然后它应该可以很好地处理 JSON 数据:

$middlewareQueue
    ->add(new \Cake\Http\Middleware\BodyParserMiddleware())
    ->add(new \Authentication\Middleware\AuthenticationMiddleware($this));
$this->loadComponent('RequestHandler', [
    'inputTypeMap' => [],
    // ...
]);

另见