You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CakePHP 5:API访问令牌与数据库哈希令牌不匹配问题

API令牌认证问题:明文令牌无法匹配哈希令牌

问题描述

开发供第三方集成的API端点,要求使用API访问令牌认证。已实现令牌生成逻辑:生成明文public_token,用DefaultPasswordHasher哈希后得到token存入数据库,期望通过「明文令牌匹配数据库哈希令牌」的密码式验证完成认证,但目前仅使用哈希后的token值能成功认证,使用明文public_token时返回FAILURE_CREDENTIALS_MISSING错误。

现有实现代码

模型beforeSave事件

public function beforeSave(EventInterface $event)
{
    $entity = $event->getData('entity');

    if ($entity->isNew()) {
        $hasher = new DefaultPasswordHasher();

        $entity->public_token = Security::hash(Security::randomBytes(32), 'sha256', false);

        $entity->token = $hasher->hash($entity->public_token);
    }
    return true;
}

application.php配置

$authenticationService->loadIdentifier('Authentication.Token');
          
$authenticationService ->loadAuthenticator('Authentication.Token', [
                'queryParam' => 'token',
                'header' => 'Authorization',
                'tokenPrefix' => 'Bearer',
              
            ]);

AppController.php配置

$this->loadComponent('Authorization.Authorization');

$this->Authentication->setConfig('authenticate', [
    'Token' => [
        'feilds' =>[
            'password' => 'token'
        ],
        'tokenField' => 'token',
        'dataField' => 'token',
        'resolver' => 'Authentication.Orm',
        'hashAlgorithm' => 'sha256',
    ],
]);

UsersController.php的login方法

public function login() 
{
    $this->Authorization->skipAuthorization();
    $result = $this->Authentication->getResult();
    if($result->isValid()){
        try {
            $this->Authorization->authorize($this->Authentication->getIdentity()->getOriginalData());
        }catch(\Exception $ex){
            $user = [
                'message' => 'you do not have the credential to access this api'
            ];
            
        } 
      $user = $result->getData(); 
    }else{
        $this->response =$this->response->withStatus(401);
        $user = [
            
            'message' => 'login Details incorrect'
        ];
       pr($result);
       exit;
    }
    $this->set('user', $user);
    $this->viewBuilder()->setOption('serialize', 'user');
    $this->viewBuilder()->setClassName("Json");

}

错误信息

调试返回结果:

Authentication\Authenticator\Result Object
(
    [_status:protected] => FAILURE_CREDENTIALS_MISSING
    [_data:protected] => 
    [_errors:protected] => Array
        (
            [0] => Login credentials not found
        )

)

令牌示例

数据库存储的令牌数据:

'token' => '$2y$10$QW2VLbNkxPIbHNhEMxy2qeNbx2/KI21ff0Hjku0mTYWOK245hOXea'
'public_token' => 'f2f7014286b2079d4e4877cd145ab3d7d3024ebaf8c438be231819448e7ab7b8'

目标请求方式

期望使用明文public_token访问API:

$http = new Client([
            'headers' => ['Authorization' => 'Bearer ' . 'f2f7014286b2079d4e4877cd145ab3d7d3024ebaf8c438be231819448e7ab7b8']
        ]);

解决方案

问题根源

  1. 配置拼写错误:'feilds' 应为 'fields',导致认证器无法识别密码字段配置。
  2. 认证逻辑不匹配:当前配置让认证器直接将明文令牌与数据库哈希token字段做等值匹配,自然无法匹配;同时错误配置hashAlgorithm为sha256,但实际token字段是用DefaultPasswordHasher(bcrypt)生成的哈希,算法不一致。
  3. 未启用哈希验证:Token认证器默认直接匹配令牌,未启用密码式哈希验证逻辑。

修复步骤

1. 修正AppController配置

修正拼写错误,并启用哈希验证,与beforeSave中的哈希逻辑保持一致:

$this->loadComponent('Authorization.Authorization');

$this->Authentication->setConfig('authenticate', [
    'Token' => [
        'fields' => [
            'password' => 'token' // 指定数据库中存储哈希令牌的字段
        ],
        'tokenField' => 'token',
        'resolver' => 'Authentication.Orm',
        // 启用哈希验证,使用与beforeSave一致的默认密码哈希器
        'hashChecker' => true,
        'hasher' => [
            'className' => 'Authentication.Default',
        ],
    ],
]);

移除hashAlgorithm配置,因为我们使用的是bcrypt哈希而非sha256,与令牌生成逻辑对齐。

2. 调整application.php的标识符配置

明确指定标识符的令牌字段,确保逻辑一致:

$authenticationService->loadIdentifier('Authentication.Token', [
    'tokenField' => 'token', // 数据库中哈希令牌的字段
]);

$authenticationService->loadAuthenticator('Authentication.Token', [
    'queryParam' => 'token',
    'header' => 'Authorization',
    'tokenPrefix' => 'Bearer',
]);

3. 修复Login方法逻辑

修正Login方法中覆盖变量的问题,确保授权失败时返回正确提示:

public function login() 
{
    $this->Authorization->skipAuthorization();
    $result = $this->Authentication->getResult();
    
    if ($result->isValid()) {
        try {
            $this->Authorization->authorize($this->Authentication->getIdentity()->getOriginalData());
            $user = $result->getData();
        } catch (\Exception $ex) {
            $this->response = $this->response->withStatus(403);
            $user = ['message' => 'You do not have the credential to access this api'];
        }
    } else {
        $this->response = $this->response->withStatus(401);
        $user = ['message' => 'Login details incorrect'];
    }
    
    $this->set('user', $user);
    $this->viewBuilder()
        ->setOption('serialize', 'user')
        ->setClassName('Json');
}

测试验证

使用明文public_token发送请求,此时认证器会自动用DefaultPasswordHasher验证明文令牌与数据库中的token哈希值,完成认证流程。

内容的提问来源于stack exchange,提问作者mopo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:33:11