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

Symfony中Behat测试登录接口失败,传JSON仍提示缺凭证

问题:Behat测试Symfony登录接口返回「缺少凭证」错误

问题背景

已有可正常运行的curl登录请求能正确匹配用户,但编写Behat测试验证该接口时,数据库用户信息配置正确、JSON数据编码无误的情况下,测试仍返回「缺少凭证」错误,Symfony的LoginController无法识别凭证。


成功的curl请求

curl --request POST 
  --url http://localhost/login 
  --header 'content-type: application/json' 
  --data '{
  "username": "my_username",
  "password": "my_password"
}'

LoginController代码

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;

class LoginController extends AbstractController
{
    #[Route('/login', name: 'app_login', methods: ['POST'])]
    public function index(#[CurrentUser] ?User $user): Response
    {
        if ($user === null) {
            return $this->json([
                'message' => 'An error occured',
                'errors' => ['missing credentials'],
            ], Response::HTTP_UNAUTHORIZED);
        }

        $token = 'RANDOM_TOKEN';

        return $this->json([
            'user' => $user->getUserIdentifier(),
            'token' => $token,
        ]);
    }
}

Behat场景及关联代码

Behat测试场景

Scenario: Login successful
 Given I am an unlogged user
    And these users already exist
        | email       | username | password |
        | test@me.com | test     | test_pwd |
    When I POST to the login route with the following JSON data:
        | username | password  |
        | test     | test_pwd  |
    Then the response status code should be 200
    And the response should be valid JSON
    And the "user" key should contain "test"
    And the "token" key should not be empty

步骤定义函数

/**
 * @When I :verb to the :route route with the following JSON data:
 */
public function iVerbToTheRouteWithJSONData(string $verb, string $route, TableNode $table): void
{
    Assert::count($table->getLines(), 2); // One for the header, one for the data
    $params = [];
    foreach ($table as $row) {
        Assert::isArray($row);
        $params = $row;
    }

    $encoded = json_encode($params);

    $this->response = $this->kernel->handle(
        Request::create($route, $verb, [], [], [], ['Content-Type' => 'application/json'], $encoded)
    );
}

错误响应

{"message":"An error occured","errors":["missing credentials"]}

解决方法

核心问题

直接调用$kernel->handle()会绕过Symfony的防火墙认证流程,导致登录凭证无法被识别。Symfony的JsonLoginAuthenticator需要完整的请求处理链(包括防火墙中间件)才能解析凭证并完成认证。

修复步骤

  1. 改用Symfony Client发送请求
    在Behat上下文类中注入Symfony\Bundle\FrameworkBundle\Client,用它模拟完整的HTTP请求流程:

    use Symfony\Bundle\FrameworkBundle\Client;
    use Symfony\Component\HttpFoundation\Response;
    use Behat\Gherkin\Node\TableNode;
    use Webmozart\Assert\Assert;
    
    class YourFeatureContext implements Context
    {
        private Client $client;
        private Response $response;
    
        public function __construct(Client $client)
        {
            $this->client = $client;
        }
    
        /**
         * @When I :verb to the :route route with the following JSON data:
         */
        public function iVerbToTheRouteWithJSONData(string $verb, string $route, TableNode $table): void
        {
            Assert::count($table->getLines(), 2);
            $params = $table->getRowsHash(); // 更简洁的表格数据提取方式
    
            $encoded = json_encode($params);
    
            $this->client->request(
                $verb,
                $route,
                [],
                [],
                ['CONTENT_TYPE' => 'application/json'],
                $encoded
            );
    
            $this->response = $this->client->getResponse();
        }
    }
    
  2. 确保用户密码加密存储
    在创建测试用户的步骤中,必须用Symfony的密码加密器处理明文密码,否则认证会失败:

    use App\Entity\User;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
    
    /**
     * @Given these users already exist
     */
    public function theseUsersAlreadyExist(TableNode $table, UserPasswordHasherInterface $passwordHasher): void
    {
        $entityManager = $this->client->getContainer()->get(EntityManagerInterface::class);
    
        foreach ($table->getHash() as $userData) {
            $user = new User();
            $user->setEmail($userData['email']);
            $user->setUsername($userData['username']);
            // 加密密码
            $hashedPassword = $passwordHasher->hashPassword($user, $userData['password']);
            $user->setPassword($hashedPassword);
    
            $entityManager->persist($user);
        }
    
        $entityManager->flush();
    }
    

原理说明

Symfony Client会模拟真实HTTP请求,触发防火墙的认证逻辑,让JsonLoginAuthenticator正常解析JSON格式的登录凭证并完成用户验证。同时,加密后的密码才能被Symfony的认证系统正确校验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:48:12